feat: improve LLMExecutor

This commit is contained in:
2026-06-02 23:42:16 +09:00
parent 545c5f388f
commit 4ed6264aed

View File

@@ -3,22 +3,31 @@ import { OpenRouter } from "@openrouter/sdk";
import type { ChatRequestEffort } from "@openrouter/sdk/models"; import type { ChatRequestEffort } from "@openrouter/sdk/models";
const CONVERSATION_MODEL = "x-ai/grok-4.3" as const; const CONVERSATION_MODEL = "x-ai/grok-4.3" as const;
const IDENTITY_MODEL = "openai/gpt-5.4-mini" as const; const IDENTITY_MODEL = "openai/gpt-5.4" as const;
type MODELS = typeof CONVERSATION_MODEL | typeof IDENTITY_MODEL; type MODELS = typeof CONVERSATION_MODEL | typeof IDENTITY_MODEL;
interface StructuredOptions { type StructuredOptions = {
instruction: string; instruction: string;
message: string; message: string;
reasoningEffort: ChatRequestEffort; reasoningEffort?: ChatRequestEffort;
jsonSchemaName: string; } & (
jsonSchema: | {
| { jsonSchemaName: string;
[k: string]: any; jsonSchema:
} | {
| undefined; [k: string]: any;
} }
| undefined;
}
| {}
);
export class LLMExecutor { export class LLMExecutor {
models = {
conversation: CONVERSATION_MODEL,
identity: IDENTITY_MODEL,
};
private apiKey: string; private apiKey: string;
client: OpenRouter; client: OpenRouter;
@@ -27,8 +36,8 @@ export class LLMExecutor {
this.client = new OpenRouter({ apiKey: this.apiKey, appTitle: "boxbrain" }); this.client = new OpenRouter({ apiKey: this.apiKey, appTitle: "boxbrain" });
} }
private structuredCall<T>(model: MODELS, options: StructuredOptions) { async call<T>(model: MODELS, options: StructuredOptions) {
this.client.chat.send({ const result = await this.client.chat.send({
chatRequest: { chatRequest: {
model, model,
messages: [ messages: [
@@ -42,19 +51,36 @@ export class LLMExecutor {
}, },
], ],
reasoning: { reasoning: {
effort: options.reasoningEffort, effort:
}, (options.reasoningEffort ?? model === IDENTITY_MODEL)
responseFormat: { ? "high"
type: "json_schema", : "low",
jsonSchema: {
name: options.jsonSchemaName,
schema: options.jsonSchema,
strict: true,
},
}, },
responseFormat:
"jsonSchemaName" in options
? {
type: "json_schema",
jsonSchema: {
name: options.jsonSchemaName,
schema: options.jsonSchema,
strict: true,
},
}
: { type: "text" },
stream: false, stream: false,
}, },
}); });
const content = result.choices[0]?.message?.content;
if (!content) {
throw new Error("Empty response from model");
}
if ("jsonSchemaName" in options) {
return JSON.parse(content) as T;
} else {
return content as T;
}
} }
} }