diff --git a/src/openrouter/index.ts b/src/openrouter/index.ts index 3c28b03..6b2830b 100644 --- a/src/openrouter/index.ts +++ b/src/openrouter/index.ts @@ -3,22 +3,31 @@ import { OpenRouter } from "@openrouter/sdk"; import type { ChatRequestEffort } from "@openrouter/sdk/models"; 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; -interface StructuredOptions { +type StructuredOptions = { instruction: string; message: string; - reasoningEffort: ChatRequestEffort; - jsonSchemaName: string; - jsonSchema: - | { - [k: string]: any; - } - | undefined; -} + reasoningEffort?: ChatRequestEffort; +} & ( + | { + jsonSchemaName: string; + jsonSchema: + | { + [k: string]: any; + } + | undefined; + } + | {} +); export class LLMExecutor { + models = { + conversation: CONVERSATION_MODEL, + identity: IDENTITY_MODEL, + }; + private apiKey: string; client: OpenRouter; @@ -27,8 +36,8 @@ export class LLMExecutor { this.client = new OpenRouter({ apiKey: this.apiKey, appTitle: "boxbrain" }); } - private structuredCall(model: MODELS, options: StructuredOptions) { - this.client.chat.send({ + async call(model: MODELS, options: StructuredOptions) { + const result = await this.client.chat.send({ chatRequest: { model, messages: [ @@ -42,19 +51,36 @@ export class LLMExecutor { }, ], reasoning: { - effort: options.reasoningEffort, - }, - responseFormat: { - type: "json_schema", - jsonSchema: { - name: options.jsonSchemaName, - schema: options.jsonSchema, - strict: true, - }, + effort: + (options.reasoningEffort ?? model === IDENTITY_MODEL) + ? "high" + : "low", }, + responseFormat: + "jsonSchemaName" in options + ? { + type: "json_schema", + jsonSchema: { + name: options.jsonSchemaName, + schema: options.jsonSchema, + strict: true, + }, + } + : { type: "text" }, 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; + } } }