@opencode-ai/ai
Advanced tools
| import { Message } from "./schema/messages.js"; | ||
| export declare function normalizeToolHistory(messages: ReadonlyArray<Message>): readonly Message[]; |
| import { Message, ToolResultPart } from "./schema/messages.js"; | ||
| const EMPTY_TOOL_OUTPUT = "(no tool output)"; | ||
| const MISSING_TOOL_RESULT = "Tool result missing"; | ||
| export function normalizeToolHistory(messages) { | ||
| const normalized = []; | ||
| const pending = new Map(); | ||
| const appendMissingResults = () => { | ||
| if (pending.size === 0) | ||
| return; | ||
| normalized.push(missingToolResults(pending.values())); | ||
| pending.clear(); | ||
| }; | ||
| for (const message of messages) { | ||
| if (message.role === "user" || message.role === "assistant") | ||
| appendMissingResults(); | ||
| if (message.role === "tool") { | ||
| const tool = normalizeToolMessage(message, pending); | ||
| if (tool) | ||
| normalized.push(tool); | ||
| continue; | ||
| } | ||
| normalized.push(message); | ||
| if (message.role !== "assistant") | ||
| continue; | ||
| for (const part of message.content) { | ||
| if (part.type === "tool-call" && part.providerExecuted !== true) | ||
| pending.set(part.id, part); | ||
| } | ||
| } | ||
| return normalized.length === messages.length && normalized.every((message, index) => message === messages[index]) | ||
| ? messages | ||
| : normalized; | ||
| } | ||
| function missingToolResults(calls) { | ||
| return new Message({ | ||
| role: "tool", | ||
| content: [...calls].map((call) => ToolResultPart.make({ id: call.id, name: call.name, result: MISSING_TOOL_RESULT, resultType: "error" })), | ||
| }); | ||
| } | ||
| function normalizeToolMessage(message, pending) { | ||
| const content = message.content.map((part) => { | ||
| if (part.type !== "tool-result" || part.providerExecuted === true) | ||
| return part; | ||
| const call = pending.get(part.id); | ||
| if (call) | ||
| pending.delete(part.id); | ||
| return normalizeToolResult(part, call?.name ?? part.name); | ||
| }); | ||
| if (content.length === 0) | ||
| return undefined; | ||
| if (content.every((part, index) => part === message.content[index])) | ||
| return message; | ||
| return new Message({ | ||
| id: message.id, | ||
| role: message.role, | ||
| content, | ||
| metadata: message.metadata, | ||
| native: message.native, | ||
| }); | ||
| } | ||
| function normalizeToolResult(part, name) { | ||
| const named = part.name === name ? part : { ...part, name }; | ||
| if (named.result.type === "text" && named.result.value === "") | ||
| return { ...named, result: { type: "text", value: EMPTY_TOOL_OUTPUT } }; | ||
| if (named.result.type === "error" && named.result.value === "") | ||
| return { ...named, result: { type: "error", value: EMPTY_TOOL_OUTPUT } }; | ||
| if (named.result.type !== "content") | ||
| return named; | ||
| const value = named.result.value.filter((item) => item.type !== "text" || item.text !== ""); | ||
| if (value.length === 0) | ||
| return { ...named, result: { type: "text", value: EMPTY_TOOL_OUTPUT } }; | ||
| if (value.length === named.result.value.length) | ||
| return named; | ||
| return { ...named, result: { type: "content", value } }; | ||
| } |
@@ -8,2 +8,3 @@ import { Cause, Context, Effect, Layer, Schema, Stream } from "effect"; | ||
| import { applyCachePolicy } from "../cache-policy.js"; | ||
| import { normalizeToolHistory } from "../tool-history.js"; | ||
| import { sanitizeSurrogates } from "../utils/sanitize.js"; | ||
@@ -51,9 +52,11 @@ import * as ProviderShared from "../protocols/shared.js"; | ||
| const resolveRequestOptions = (request) => { | ||
| const routeDefaults = request.model.route.defaults; | ||
| const modelDefaults = request.model.defaults; | ||
| const generation = mergeGenerationOptions(routeDefaults.generation, modelDefaults?.generation, request.generation); | ||
| return LLMRequest.update(request, { | ||
| const messages = normalizeToolHistory(request.messages); | ||
| const normalized = messages === request.messages ? request : LLMRequest.update(request, { messages }); | ||
| const routeDefaults = normalized.model.route.defaults; | ||
| const modelDefaults = normalized.model.defaults; | ||
| const generation = mergeGenerationOptions(routeDefaults.generation, modelDefaults?.generation, normalized.generation); | ||
| return LLMRequest.update(normalized, { | ||
| generation: generation ?? new GenerationOptions({}), | ||
| providerOptions: mergeProviderOptions(routeDefaults.providerOptions, modelDefaults?.providerOptions, request.providerOptions), | ||
| http: mergeHttpOptions(routeDefaults.http, modelDefaults?.http, request.http), | ||
| providerOptions: mergeProviderOptions(routeDefaults.providerOptions, modelDefaults?.providerOptions, normalized.providerOptions), | ||
| http: mergeHttpOptions(routeDefaults.http, modelDefaults?.http, normalized.http), | ||
| }); | ||
@@ -60,0 +63,0 @@ }; |
+3
-3
| { | ||
| "$schema": "https://json.schemastore.org/package.json", | ||
| "version": "0.0.0-dev-18709", | ||
| "version": "0.0.0-dev-18710", | ||
| "name": "@opencode-ai/ai", | ||
@@ -33,3 +33,3 @@ "type": "module", | ||
| "@effect/platform-node": "4.0.0-rc.112", | ||
| "@opencode-ai/http-recorder": "0.0.0-dev-18709", | ||
| "@opencode-ai/http-recorder": "0.0.0-dev-18710", | ||
| "@tsconfig/bun": "1.0.9", | ||
@@ -43,3 +43,3 @@ "@types/bun": "1.3.13", | ||
| "@smithy/util-utf8": "4.2.2", | ||
| "@opencode-ai/schema": "0.0.0-dev-18709", | ||
| "@opencode-ai/schema": "0.0.0-dev-18710", | ||
| "aws4fetch": "1.0.20", | ||
@@ -46,0 +46,0 @@ "effect": "4.0.0-rc.112", |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
1550228
0.22%218
0.93%35743
0.22%+ Added
- Removed