https://github.com/decolua/9router/pull/1166
This commit is contained in:
@@ -5,6 +5,12 @@
|
||||
import { register } from "../index.js";
|
||||
import { FORMATS } from "../formats.js";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import {
|
||||
resolveKiroModel,
|
||||
isThinkingEnabled,
|
||||
buildThinkingSystemPrefix,
|
||||
KIRO_AGENTIC_SYSTEM_PROMPT
|
||||
} from "../../config/kiroConstants.js";
|
||||
|
||||
/**
|
||||
* Convert OpenAI messages to Kiro format
|
||||
@@ -282,6 +288,20 @@ function convertMessages(messages, tools, model) {
|
||||
|
||||
/**
|
||||
* Build Kiro payload from OpenAI format
|
||||
*
|
||||
* Two 9router-specific behaviours implemented here:
|
||||
*
|
||||
* 1. `-agentic` model suffix. Synthetic variant — same upstream model, but we
|
||||
* inject a chunked-write system prompt to keep large file writes under
|
||||
* Kiro's 2-3 minute server timeout. The suffix is stripped before being
|
||||
* sent upstream.
|
||||
*
|
||||
* 2. Thinking / reasoning. Kiro does not accept `thinking.type` or
|
||||
* `reasoning_effort` natively. The only way to enable reasoning is to
|
||||
* inject `<thinking_mode>enabled</thinking_mode>` into the user content
|
||||
* sent upstream. Detection covers Anthropic-Beta header, Claude API
|
||||
* `thinking`, OpenAI `reasoning_effort`, AMP/Cursor magic tags, and model
|
||||
* name hints.
|
||||
*/
|
||||
export function buildKiroPayload(model, body, stream, credentials) {
|
||||
const messages = body.messages || [];
|
||||
@@ -290,14 +310,29 @@ export function buildKiroPayload(model, body, stream, credentials) {
|
||||
const temperature = body.temperature;
|
||||
const topP = body.top_p;
|
||||
|
||||
const { history, currentMessage } = convertMessages(messages, tools, model);
|
||||
const { upstream: upstreamModel, agentic, thinking: modelImpliesThinking } = resolveKiroModel(model);
|
||||
const thinkingEnabled = modelImpliesThinking || isThinkingEnabled(body, null, model);
|
||||
|
||||
const { history, currentMessage } = convertMessages(messages, tools, upstreamModel);
|
||||
|
||||
const profileArn = credentials?.providerSpecificData?.profileArn || "";
|
||||
|
||||
let finalContent = currentMessage?.userInputMessage?.content || "";
|
||||
const timestamp = new Date().toISOString();
|
||||
finalContent = `[Context: Current time is ${timestamp}]\n\n${finalContent}`;
|
||||
|
||||
|
||||
// Build the system-prompt prefix that goes ABOVE the user message body.
|
||||
// Order: thinking_mode tag first (so Kiro sees it before any user text),
|
||||
// then context/timestamp marker, then optional agentic chunked-write prompt.
|
||||
const prefixParts = [];
|
||||
if (thinkingEnabled) {
|
||||
prefixParts.push(buildThinkingSystemPrefix());
|
||||
}
|
||||
prefixParts.push(`[Context: Current time is ${timestamp}]`);
|
||||
if (agentic) {
|
||||
prefixParts.push(KIRO_AGENTIC_SYSTEM_PROMPT);
|
||||
}
|
||||
finalContent = `${prefixParts.join("\n\n")}\n\n${finalContent}`;
|
||||
|
||||
const payload = {
|
||||
conversationState: {
|
||||
chatTriggerType: "MANUAL",
|
||||
@@ -305,8 +340,11 @@ export function buildKiroPayload(model, body, stream, credentials) {
|
||||
currentMessage: {
|
||||
userInputMessage: {
|
||||
content: finalContent,
|
||||
modelId: model,
|
||||
modelId: upstreamModel,
|
||||
origin: "AI_EDITOR",
|
||||
...(currentMessage?.userInputMessage?.images?.length > 0 && {
|
||||
images: currentMessage.userInputMessage.images
|
||||
}),
|
||||
...(currentMessage?.userInputMessage?.userInputMessageContext && {
|
||||
userInputMessageContext: currentMessage.userInputMessage.userInputMessageContext
|
||||
})
|
||||
@@ -327,6 +365,12 @@ export function buildKiroPayload(model, body, stream, credentials) {
|
||||
if (topP !== undefined) payload.inferenceConfig.topP = topP;
|
||||
}
|
||||
|
||||
// Tag payload so the executor can route the upstream model id correctly.
|
||||
Object.defineProperty(payload, "_kiroUpstreamModel", {
|
||||
value: upstreamModel,
|
||||
enumerable: false
|
||||
});
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
|
||||
@@ -85,12 +85,18 @@ export function convertKiroToOpenAI(chunk, state) {
|
||||
return openaiChunk;
|
||||
}
|
||||
|
||||
// Handle reasoning/thinking events
|
||||
// Handle reasoning/thinking events.
|
||||
// Kiro emits reasoningContentEvent when the request enabled thinking via
|
||||
// the <thinking_mode>enabled</thinking_mode> system-prompt tag. We surface
|
||||
// this as OpenAI delta.reasoning_content so downstream translators can map
|
||||
// it to Claude thinking blocks / Anthropic reasoning / etc.
|
||||
if (eventType === "reasoningContentEvent" || data.reasoningContentEvent) {
|
||||
const content = data.reasoningContentEvent?.content || data.content || "";
|
||||
const reasoning = data.reasoningContentEvent || data;
|
||||
const content = (typeof reasoning === "string")
|
||||
? reasoning
|
||||
: (reasoning.text || reasoning.content || data.content || "");
|
||||
if (!content) return null;
|
||||
|
||||
// Convert to thinking block format (Claude-style)
|
||||
const openaiChunk = {
|
||||
id: state.responseId,
|
||||
object: "chat.completion.chunk",
|
||||
@@ -100,7 +106,7 @@ export function convertKiroToOpenAI(chunk, state) {
|
||||
index: 0,
|
||||
delta: {
|
||||
...(state.chunkIndex === 0 ? { role: "assistant" } : {}),
|
||||
content: `<thinking>${content}</thinking>`
|
||||
reasoning_content: content
|
||||
},
|
||||
finish_reason: null
|
||||
}]
|
||||
|
||||
Reference in New Issue
Block a user