From 64b1fa22e5a9c2dd70209193c5aed9982c59b223 Mon Sep 17 00:00:00 2001 From: p-sw Date: Wed, 10 Jun 2026 23:39:45 +0900 Subject: [PATCH] feat: implement chatWithTools --- src/openrouter/index.ts | 56 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/src/openrouter/index.ts b/src/openrouter/index.ts index 793ab59..f6e4264 100644 --- a/src/openrouter/index.ts +++ b/src/openrouter/index.ts @@ -1,6 +1,12 @@ import { config } from "@/config"; import { OpenRouter } from "@openrouter/sdk"; -import type { ChatRequestEffort } from "@openrouter/sdk/models"; +import type { + ChatAssistantMessage, + ChatChoice, + ChatFunctionTool, + ChatMessages, + 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; @@ -22,6 +28,14 @@ type StructuredOptions = { | {} ); +type ChatWithToolsOptions = { + instruction: string; + messages: ChatMessages[]; + tools: ChatFunctionTool[]; + reasoningEffort?: ChatRequestEffort; + parallelToolCalls?: boolean; +}; + export class LLMExecutor { models = { conversation: CONVERSATION_MODEL, @@ -81,6 +95,46 @@ export class LLMExecutor { return content as T; } } + + async chatWithTools( + model: MODELS, + options: ChatWithToolsOptions, + ): Promise { + const result = await this.client.chat.send({ + chatRequest: { + model, + messages: [ + { + role: "system", + content: options.instruction, + }, + ...options.messages, + ], + reasoning: { + effort: + options.reasoningEffort ?? + (model === IDENTITY_MODEL ? "medium" : "none"), + }, + responseFormat: { type: "text" }, + tools: options.tools, + parallelToolCalls: options.parallelToolCalls ?? false, + stream: false, + }, + }); + + const choice = result.choices[0]; + if (!choice) { + throw new Error("LLM returned no choice"); + } + return choice; + } } +export type { + ChatAssistantMessage, + ChatChoice, + ChatFunctionTool, + ChatMessages, +}; + export const llm = new LLMExecutor(config.openrouterApiKey);