@caupulican/pi-ai 0.81.39 → 0.81.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/image-models.generated.d.ts +15 -0
- package/dist/image-models.generated.d.ts.map +1 -1
- package/dist/image-models.generated.js +15 -0
- package/dist/image-models.generated.js.map +1 -1
- package/dist/models.d.ts +7 -0
- package/dist/models.d.ts.map +1 -1
- package/dist/models.generated.d.ts +669 -44
- package/dist/models.generated.d.ts.map +1 -1
- package/dist/models.generated.js +725 -116
- package/dist/models.generated.js.map +1 -1
- package/dist/models.js +9 -0
- package/dist/models.js.map +1 -1
- package/dist/providers/amazon-bedrock.d.ts.map +1 -1
- package/dist/providers/amazon-bedrock.js +16 -10
- package/dist/providers/amazon-bedrock.js.map +1 -1
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +13 -21
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/azure-openai-responses.d.ts.map +1 -1
- package/dist/providers/azure-openai-responses.js +6 -4
- package/dist/providers/azure-openai-responses.js.map +1 -1
- package/dist/providers/faux.d.ts +1 -0
- package/dist/providers/faux.d.ts.map +1 -1
- package/dist/providers/faux.js +1 -0
- package/dist/providers/faux.js.map +1 -1
- package/dist/providers/mistral.d.ts.map +1 -1
- package/dist/providers/mistral.js +20 -15
- package/dist/providers/mistral.js.map +1 -1
- package/dist/providers/openai-codex-responses.d.ts.map +1 -1
- package/dist/providers/openai-codex-responses.js +14 -5
- package/dist/providers/openai-codex-responses.js.map +1 -1
- package/dist/providers/openai-completions.d.ts.map +1 -1
- package/dist/providers/openai-completions.js +1 -1
- package/dist/providers/openai-completions.js.map +1 -1
- package/dist/providers/openai-responses-shared.d.ts +5 -0
- package/dist/providers/openai-responses-shared.d.ts.map +1 -1
- package/dist/providers/openai-responses-shared.js +24 -4
- package/dist/providers/openai-responses-shared.js.map +1 -1
- package/dist/providers/openai-responses.d.ts.map +1 -1
- package/dist/providers/openai-responses.js +6 -3
- package/dist/providers/openai-responses.js.map +1 -1
- package/dist/utils/tool-names.d.ts +6 -1
- package/dist/utils/tool-names.d.ts.map +1 -1
- package/dist/utils/tool-names.js +22 -9
- package/dist/utils/tool-names.js.map +1 -1
- package/dist/utils/tool-repair/text-protocol.d.ts +3 -0
- package/dist/utils/tool-repair/text-protocol.d.ts.map +1 -1
- package/dist/utils/tool-repair/text-protocol.js +10 -0
- package/dist/utils/tool-repair/text-protocol.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai-responses.js","sourceRoot":"","sources":["../../src/providers/openai-responses.ts"],"names":[],"mappings":"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","sourcesContent":["import OpenAI from \"openai\";\nimport type { ResponseCreateParamsStreaming } from \"openai/resources/responses/responses.js\";\nimport { clampThinkingLevel } from \"../models.ts\";\nimport type {\n\tApi,\n\tAssistantMessage,\n\tCacheRetention,\n\tContext,\n\tModel,\n\tOpenAIResponsesCompat,\n\tSimpleStreamOptions,\n\tStreamFunction,\n\tStreamOptions,\n\tUsage,\n} from \"../types.ts\";\nimport { formatProviderError, normalizeProviderError } from \"../utils/error-body.ts\";\nimport { AssistantMessageEventStream } from \"../utils/event-stream.ts\";\nimport { headersToRecord } from \"../utils/headers.ts\";\nimport { isCloudflareProvider, resolveCloudflareBaseUrl } from \"./cloudflare.ts\";\nimport { buildCopilotDynamicHeaders, hasCopilotVisionInput } from \"./github-copilot-headers.ts\";\nimport { clampOpenAIPromptCacheKey } from \"./openai-prompt-cache.ts\";\nimport {\n\tbuildResponsesInstructions,\n\tconvertResponsesMessages,\n\tconvertResponsesTools,\n\tprocessResponsesStream,\n} from \"./openai-responses-shared.ts\";\nimport { buildBaseOptions } from \"./simple-options.ts\";\n\nconst OPENAI_TOOL_CALL_PROVIDERS = new Set([\"openai\", \"openai-codex\", \"opencode\", \"fugu\"]);\nconst FUGU_DEFAULT_TIMEOUT_MS = 7_200_000;\nconst FUGU_DEFAULT_MAX_RETRIES = 4;\n\n/**\n * Resolve cache retention preference.\n * Defaults to \"short\" and uses PI_CACHE_RETENTION for backward compatibility.\n */\nfunction resolveCacheRetention(cacheRetention?: CacheRetention): CacheRetention {\n\tif (cacheRetention) {\n\t\treturn cacheRetention;\n\t}\n\tif (typeof process !== \"undefined\" && process.env.PI_CACHE_RETENTION === \"long\") {\n\t\treturn \"long\";\n\t}\n\treturn \"short\";\n}\n\nfunction getCompat(model: Model<\"openai-responses\">): Required<OpenAIResponsesCompat> {\n\treturn {\n\t\tsendSessionIdHeader: model.compat?.sendSessionIdHeader ?? true,\n\t\tsupportsLongCacheRetention: model.compat?.supportsLongCacheRetention ?? true,\n\t};\n}\n\nfunction getPromptCacheRetention(\n\tcompat: Required<OpenAIResponsesCompat>,\n\tcacheRetention: CacheRetention,\n): \"24h\" | undefined {\n\treturn cacheRetention === \"long\" && compat.supportsLongCacheRetention ? \"24h\" : undefined;\n}\n\nfunction formatOpenAIResponsesError(error: unknown): string {\n\treturn formatProviderError(normalizeProviderError(error), \"OpenAI API error\");\n}\n\n// OpenAI Responses-specific options\nexport interface OpenAIResponsesOptions extends StreamOptions {\n\treasoningEffort?: \"none\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\" | \"max\" | \"ultra\";\n\treasoningSummary?: \"auto\" | \"detailed\" | \"concise\" | null;\n\tserviceTier?: ResponseCreateParamsStreaming[\"service_tier\"];\n}\n\n/**\n * Generate function for OpenAI Responses API\n */\nexport const streamOpenAIResponses: StreamFunction<\"openai-responses\", OpenAIResponsesOptions> = (\n\tmodel: Model<\"openai-responses\">,\n\tcontext: Context,\n\toptions?: OpenAIResponsesOptions,\n): AssistantMessageEventStream => {\n\tconst stream = new AssistantMessageEventStream();\n\n\t// Start async processing\n\t(async () => {\n\t\tconst output: AssistantMessage = {\n\t\t\trole: \"assistant\",\n\t\t\tcontent: [],\n\t\t\tapi: model.api as Api,\n\t\t\tprovider: model.provider,\n\t\t\tmodel: model.id,\n\t\t\tusage: {\n\t\t\t\tinput: 0,\n\t\t\t\toutput: 0,\n\t\t\t\tcacheRead: 0,\n\t\t\t\tcacheWrite: 0,\n\t\t\t\ttotalTokens: 0,\n\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t},\n\t\t\tstopReason: \"stop\",\n\t\t\ttimestamp: Date.now(),\n\t\t};\n\n\t\ttry {\n\t\t\t// Create OpenAI client\n\t\t\tconst apiKey = options?.apiKey;\n\t\t\tif (!apiKey) {\n\t\t\t\tthrow new Error(`No API key for provider: ${model.provider}`);\n\t\t\t}\n\t\t\tconst cacheRetention = resolveCacheRetention(options?.cacheRetention);\n\t\t\tconst cacheSessionId = cacheRetention === \"none\" ? undefined : options?.sessionId;\n\t\t\tconst client = createClient(model, context, apiKey, options?.headers, cacheSessionId);\n\t\t\tlet params = buildParams(model, context, options);\n\t\t\tconst nextParams = await options?.onPayload?.(params, model);\n\t\t\tif (nextParams !== undefined) {\n\t\t\t\tparams = nextParams as ResponseCreateParamsStreaming;\n\t\t\t}\n\t\t\tconst requestOptions = {\n\t\t\t\t...(options?.signal ? { signal: options.signal } : {}),\n\t\t\t\t...(options?.timeoutMs !== undefined\n\t\t\t\t\t? { timeout: options.timeoutMs }\n\t\t\t\t\t: model.provider === \"fugu\"\n\t\t\t\t\t\t? { timeout: FUGU_DEFAULT_TIMEOUT_MS }\n\t\t\t\t\t\t: {}),\n\t\t\t\tmaxRetries: options?.maxRetries ?? (model.provider === \"fugu\" ? FUGU_DEFAULT_MAX_RETRIES : 0),\n\t\t\t};\n\t\t\tconst { data: openaiStream, response } = await client.responses.create(params, requestOptions).withResponse();\n\t\t\tawait options?.onResponse?.({ status: response.status, headers: headersToRecord(response.headers) }, model);\n\t\t\tstream.push({ type: \"start\", partial: output });\n\n\t\t\tawait processResponsesStream(openaiStream, output, stream, model, {\n\t\t\t\tserviceTier: options?.serviceTier,\n\t\t\t\tapplyServiceTierPricing:\n\t\t\t\t\tmodel.provider === \"fugu\"\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: (usage, serviceTier) => applyServiceTierPricing(usage, serviceTier, model),\n\t\t\t});\n\n\t\t\tif (options?.signal?.aborted) {\n\t\t\t\tthrow new Error(\"Request was aborted\");\n\t\t\t}\n\n\t\t\tif (output.stopReason === \"aborted\" || output.stopReason === \"error\") {\n\t\t\t\tthrow new Error(\"An unknown error occurred\");\n\t\t\t}\n\n\t\t\tstream.push({ type: \"done\", reason: output.stopReason, message: output });\n\t\t\tstream.end();\n\t\t} catch (error) {\n\t\t\tfor (const block of output.content) {\n\t\t\t\tdelete (block as { index?: number }).index;\n\t\t\t\t// partialJson is only a streaming scratch buffer; never persist it.\n\t\t\t\tdelete (block as { partialJson?: string }).partialJson;\n\t\t\t}\n\t\t\toutput.stopReason = options?.signal?.aborted ? \"aborted\" : \"error\";\n\t\t\toutput.errorMessage = formatOpenAIResponsesError(error);\n\t\t\tstream.push({ type: \"error\", reason: output.stopReason, error: output });\n\t\t\tstream.end();\n\t\t}\n\t})();\n\n\treturn stream;\n};\n\nexport const streamSimpleOpenAIResponses: StreamFunction<\"openai-responses\", SimpleStreamOptions> = (\n\tmodel: Model<\"openai-responses\">,\n\tcontext: Context,\n\toptions?: SimpleStreamOptions,\n): AssistantMessageEventStream => {\n\tconst apiKey = options?.apiKey;\n\tif (!apiKey) {\n\t\tthrow new Error(`No API key for provider: ${model.provider}`);\n\t}\n\n\tconst base = buildBaseOptions(model, options, apiKey);\n\tconst requestedReasoning = options?.reasoning ?? model.defaultThinkingLevel;\n\tconst clampedReasoning = requestedReasoning ? clampThinkingLevel(model, requestedReasoning) : undefined;\n\tconst reasoningEffort = clampedReasoning === \"off\" ? \"none\" : clampedReasoning;\n\n\treturn streamOpenAIResponses(model, context, {\n\t\t...base,\n\t\treasoningEffort,\n\t} satisfies OpenAIResponsesOptions);\n};\n\nfunction resolveFuguBaseUrl(): string | undefined {\n\tif (typeof process === \"undefined\") return undefined;\n\tconst value = process.env.FUGU_BASE_URL?.trim();\n\tif (!value) return undefined;\n\tconst baseUrl = value.replace(/\\/+$/, \"\");\n\treturn baseUrl.endsWith(\"/v1\") ? baseUrl : `${baseUrl}/v1`;\n}\n\nfunction resolveBaseUrl(model: Model<\"openai-responses\">): string {\n\tif (isCloudflareProvider(model.provider)) return resolveCloudflareBaseUrl(model);\n\tif (model.provider === \"fugu\") return resolveFuguBaseUrl() ?? model.baseUrl;\n\treturn model.baseUrl;\n}\n\nfunction createClient(\n\tmodel: Model<\"openai-responses\">,\n\tcontext: Context,\n\tapiKey: string,\n\toptionsHeaders?: Record<string, string>,\n\tsessionId?: string,\n) {\n\tconst compat = getCompat(model);\n\tconst headers = { ...model.headers };\n\tif (model.provider === \"github-copilot\") {\n\t\tconst hasImages = hasCopilotVisionInput(context.messages);\n\t\tconst copilotHeaders = buildCopilotDynamicHeaders({\n\t\t\tmessages: context.messages,\n\t\t\thasImages,\n\t\t});\n\t\tObject.assign(headers, copilotHeaders);\n\t}\n\n\tif (sessionId) {\n\t\tif (compat.sendSessionIdHeader) {\n\t\t\theaders.session_id = sessionId;\n\t\t}\n\t\theaders[\"x-client-request-id\"] = sessionId;\n\t}\n\n\t// Merge options headers last so they can override defaults\n\tif (optionsHeaders) {\n\t\tObject.assign(headers, optionsHeaders);\n\t}\n\n\tconst defaultHeaders =\n\t\tmodel.provider === \"cloudflare-ai-gateway\"\n\t\t\t? {\n\t\t\t\t\t...headers,\n\t\t\t\t\tAuthorization: headers.Authorization ?? null,\n\t\t\t\t\t\"cf-aig-authorization\": `Bearer ${apiKey}`,\n\t\t\t\t}\n\t\t\t: headers;\n\n\treturn new OpenAI({\n\t\tapiKey,\n\t\tbaseURL: resolveBaseUrl(model),\n\t\tdangerouslyAllowBrowser: true,\n\t\tdefaultHeaders,\n\t});\n}\n\nfunction buildParams(model: Model<\"openai-responses\">, context: Context, options?: OpenAIResponsesOptions) {\n\tconst messages = convertResponsesMessages(model, context, OPENAI_TOOL_CALL_PROVIDERS);\n\n\tconst cacheRetention = resolveCacheRetention(options?.cacheRetention);\n\tconst compat = getCompat(model);\n\tconst isGpt56 = model.provider === \"openai\" && model.id.startsWith(\"gpt-5.6\");\n\tconst params: ResponseCreateParamsStreaming = {\n\t\tmodel: model.id,\n\t\tinstructions: buildResponsesInstructions(context),\n\t\tinput: messages,\n\t\tstream: true,\n\t\tprompt_cache_key: cacheRetention === \"none\" ? undefined : clampOpenAIPromptCacheKey(options?.sessionId),\n\t\t...(cacheRetention !== \"none\" && isGpt56\n\t\t\t? { prompt_cache_options: options?.promptCacheOptions ?? { mode: \"implicit\" as const, ttl: \"30m\" as const } }\n\t\t\t: {}),\n\t\t...(isGpt56 ? {} : { prompt_cache_retention: getPromptCacheRetention(compat, cacheRetention) }),\n\t\tstore: false,\n\t};\n\tconst safetyIdentifier = options?.metadata?.safety_identifier;\n\tif (typeof safetyIdentifier === \"string\" && safetyIdentifier.length > 0) {\n\t\tparams.safety_identifier = safetyIdentifier;\n\t}\n\n\tif (options?.maxTokens) {\n\t\tparams.max_output_tokens = options?.maxTokens;\n\t}\n\n\tif (options?.temperature !== undefined) {\n\t\tparams.temperature = options?.temperature;\n\t}\n\n\tif (options?.serviceTier !== undefined && model.provider !== \"fugu\") {\n\t\tparams.service_tier = options.serviceTier;\n\t}\n\n\tif (context.tools && context.tools.length > 0) {\n\t\tparams.tools = convertResponsesTools(context.tools);\n\t}\n\n\tif (model.reasoning) {\n\t\tif (\n\t\t\toptions?.reasoningEffort ||\n\t\t\toptions?.reasoningSummary !== undefined ||\n\t\t\toptions?.reasoningMode ||\n\t\t\toptions?.reasoningContext\n\t\t) {\n\t\t\tconst effort = options?.reasoningEffort\n\t\t\t\t? options.reasoningEffort === \"none\"\n\t\t\t\t\t? (model.thinkingLevelMap?.off ?? \"none\")\n\t\t\t\t\t: (model.thinkingLevelMap?.[options.reasoningEffort] ?? options.reasoningEffort)\n\t\t\t\t: \"medium\";\n\t\t\tconst reasoningDisabled = options?.reasoningEffort === \"none\";\n\t\t\tconst supportsReasoningSummary = model.provider !== \"fugu\" || model.id === \"fugu-ultra\";\n\t\t\tparams.reasoning = {\n\t\t\t\teffort: effort as NonNullable<typeof params.reasoning>[\"effort\"],\n\t\t\t\t...(supportsReasoningSummary && !reasoningDisabled && options?.reasoningSummary !== null\n\t\t\t\t\t? { summary: options?.reasoningSummary ?? \"auto\" }\n\t\t\t\t\t: {}),\n\t\t\t\t...(options?.reasoningMode ? { mode: options.reasoningMode } : {}),\n\t\t\t\t...(options?.reasoningContext ? { context: options.reasoningContext } : {}),\n\t\t\t};\n\t\t\tif (supportsReasoningSummary && !reasoningDisabled) {\n\t\t\t\tparams.include = [\"reasoning.encrypted_content\"];\n\t\t\t}\n\t\t} else if (model.provider !== \"github-copilot\" && model.thinkingLevelMap?.off !== null) {\n\t\t\tparams.reasoning = {\n\t\t\t\teffort: (model.thinkingLevelMap?.off ?? \"none\") as NonNullable<typeof params.reasoning>[\"effort\"],\n\t\t\t};\n\t\t}\n\t}\n\n\treturn params;\n}\n\nfunction getServiceTierCostMultiplier(\n\tmodel: Pick<Model<\"openai-responses\">, \"id\">,\n\tserviceTier: ResponseCreateParamsStreaming[\"service_tier\"] | undefined,\n): number {\n\tswitch (serviceTier) {\n\t\tcase \"flex\":\n\t\t\treturn 0.5;\n\t\tcase \"priority\":\n\t\t\treturn model.id === \"gpt-5.5\" ? 2.5 : 2;\n\t\tdefault:\n\t\t\treturn 1;\n\t}\n}\n\nfunction applyServiceTierPricing(\n\tusage: Usage,\n\tserviceTier: ResponseCreateParamsStreaming[\"service_tier\"] | undefined,\n\tmodel: Pick<Model<\"openai-responses\">, \"id\">,\n) {\n\tconst multiplier = getServiceTierCostMultiplier(model, serviceTier);\n\tif (multiplier === 1) return;\n\n\tusage.cost.input *= multiplier;\n\tusage.cost.output *= multiplier;\n\tusage.cost.cacheRead *= multiplier;\n\tusage.cost.cacheWrite *= multiplier;\n\tusage.cost.total = usage.cost.input + usage.cost.output + usage.cost.cacheRead + usage.cost.cacheWrite;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"openai-responses.js","sourceRoot":"","sources":["../../src/providers/openai-responses.ts"],"names":[],"mappings":"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","sourcesContent":["import OpenAI from \"openai\";\nimport type { ResponseCreateParamsStreaming } from \"openai/resources/responses/responses.js\";\nimport { clampThinkingLevel } from \"../models.ts\";\nimport type {\n\tApi,\n\tAssistantMessage,\n\tCacheRetention,\n\tContext,\n\tModel,\n\tOpenAIResponsesCompat,\n\tSimpleStreamOptions,\n\tStreamFunction,\n\tStreamOptions,\n\tUsage,\n} from \"../types.ts\";\nimport { formatProviderError, normalizeProviderError } from \"../utils/error-body.ts\";\nimport { AssistantMessageEventStream } from \"../utils/event-stream.ts\";\nimport { headersToRecord } from \"../utils/headers.ts\";\nimport { isCloudflareProvider, resolveCloudflareBaseUrl } from \"./cloudflare.ts\";\nimport { buildCopilotDynamicHeaders, hasCopilotVisionInput } from \"./github-copilot-headers.ts\";\nimport { clampOpenAIPromptCacheKey } from \"./openai-prompt-cache.ts\";\nimport {\n\tbuildResponsesInstructions,\n\tconvertResponsesMessages,\n\tconvertResponsesTools,\n\tcreateOpenAIResponsesToolNameMap,\n\tprocessResponsesStream,\n} from \"./openai-responses-shared.ts\";\nimport { buildBaseOptions } from \"./simple-options.ts\";\n\nconst OPENAI_TOOL_CALL_PROVIDERS = new Set([\"openai\", \"openai-codex\", \"opencode\", \"fugu\"]);\nconst FUGU_DEFAULT_TIMEOUT_MS = 7_200_000;\nconst FUGU_DEFAULT_MAX_RETRIES = 4;\n\n/**\n * Resolve cache retention preference.\n * Defaults to \"short\" and uses PI_CACHE_RETENTION for backward compatibility.\n */\nfunction resolveCacheRetention(cacheRetention?: CacheRetention): CacheRetention {\n\tif (cacheRetention) {\n\t\treturn cacheRetention;\n\t}\n\tif (typeof process !== \"undefined\" && process.env.PI_CACHE_RETENTION === \"long\") {\n\t\treturn \"long\";\n\t}\n\treturn \"short\";\n}\n\nfunction getCompat(model: Model<\"openai-responses\">): Required<OpenAIResponsesCompat> {\n\treturn {\n\t\tsendSessionIdHeader: model.compat?.sendSessionIdHeader ?? true,\n\t\tsupportsLongCacheRetention: model.compat?.supportsLongCacheRetention ?? true,\n\t};\n}\n\nfunction getPromptCacheRetention(\n\tcompat: Required<OpenAIResponsesCompat>,\n\tcacheRetention: CacheRetention,\n): \"24h\" | undefined {\n\treturn cacheRetention === \"long\" && compat.supportsLongCacheRetention ? \"24h\" : undefined;\n}\n\nfunction formatOpenAIResponsesError(error: unknown): string {\n\treturn formatProviderError(normalizeProviderError(error), \"OpenAI API error\");\n}\n\n// OpenAI Responses-specific options\nexport interface OpenAIResponsesOptions extends StreamOptions {\n\treasoningEffort?: \"none\" | \"minimal\" | \"low\" | \"medium\" | \"high\" | \"xhigh\" | \"max\" | \"ultra\";\n\treasoningSummary?: \"auto\" | \"detailed\" | \"concise\" | null;\n\tserviceTier?: ResponseCreateParamsStreaming[\"service_tier\"];\n}\n\n/**\n * Generate function for OpenAI Responses API\n */\nexport const streamOpenAIResponses: StreamFunction<\"openai-responses\", OpenAIResponsesOptions> = (\n\tmodel: Model<\"openai-responses\">,\n\tcontext: Context,\n\toptions?: OpenAIResponsesOptions,\n): AssistantMessageEventStream => {\n\tconst stream = new AssistantMessageEventStream();\n\n\t// Start async processing\n\t(async () => {\n\t\tconst output: AssistantMessage = {\n\t\t\trole: \"assistant\",\n\t\t\tcontent: [],\n\t\t\tapi: model.api as Api,\n\t\t\tprovider: model.provider,\n\t\t\tmodel: model.id,\n\t\t\tusage: {\n\t\t\t\tinput: 0,\n\t\t\t\toutput: 0,\n\t\t\t\tcacheRead: 0,\n\t\t\t\tcacheWrite: 0,\n\t\t\t\ttotalTokens: 0,\n\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t},\n\t\t\tstopReason: \"stop\",\n\t\t\ttimestamp: Date.now(),\n\t\t};\n\n\t\ttry {\n\t\t\t// Create OpenAI client\n\t\t\tconst apiKey = options?.apiKey;\n\t\t\tif (!apiKey) {\n\t\t\t\tthrow new Error(`No API key for provider: ${model.provider}`);\n\t\t\t}\n\t\t\tconst cacheRetention = resolveCacheRetention(options?.cacheRetention);\n\t\t\tconst cacheSessionId = cacheRetention === \"none\" ? undefined : options?.sessionId;\n\t\t\tconst client = createClient(model, context, apiKey, options?.headers, cacheSessionId);\n\t\t\tconst toolNameMap = createOpenAIResponsesToolNameMap(context.tools ?? []);\n\t\t\tlet params = buildParams(model, context, options);\n\t\t\tconst nextParams = await options?.onPayload?.(params, model);\n\t\t\tif (nextParams !== undefined) {\n\t\t\t\tparams = nextParams as ResponseCreateParamsStreaming;\n\t\t\t}\n\t\t\tconst requestOptions = {\n\t\t\t\t...(options?.signal ? { signal: options.signal } : {}),\n\t\t\t\t...(options?.timeoutMs !== undefined\n\t\t\t\t\t? { timeout: options.timeoutMs }\n\t\t\t\t\t: model.provider === \"fugu\"\n\t\t\t\t\t\t? { timeout: FUGU_DEFAULT_TIMEOUT_MS }\n\t\t\t\t\t\t: {}),\n\t\t\t\tmaxRetries: options?.maxRetries ?? (model.provider === \"fugu\" ? FUGU_DEFAULT_MAX_RETRIES : 0),\n\t\t\t};\n\t\t\tconst { data: openaiStream, response } = await client.responses.create(params, requestOptions).withResponse();\n\t\t\tawait options?.onResponse?.({ status: response.status, headers: headersToRecord(response.headers) }, model);\n\t\t\tstream.push({ type: \"start\", partial: output });\n\n\t\t\tawait processResponsesStream(openaiStream, output, stream, model, {\n\t\t\t\ttoolNameMap,\n\t\t\t\tserviceTier: options?.serviceTier,\n\t\t\t\tapplyServiceTierPricing:\n\t\t\t\t\tmodel.provider === \"fugu\"\n\t\t\t\t\t\t? undefined\n\t\t\t\t\t\t: (usage, serviceTier) => applyServiceTierPricing(usage, serviceTier, model),\n\t\t\t});\n\n\t\t\tif (options?.signal?.aborted) {\n\t\t\t\tthrow new Error(\"Request was aborted\");\n\t\t\t}\n\n\t\t\tif (output.stopReason === \"aborted\" || output.stopReason === \"error\") {\n\t\t\t\tthrow new Error(\"An unknown error occurred\");\n\t\t\t}\n\n\t\t\tstream.push({ type: \"done\", reason: output.stopReason, message: output });\n\t\t\tstream.end();\n\t\t} catch (error) {\n\t\t\tfor (const block of output.content) {\n\t\t\t\tdelete (block as { index?: number }).index;\n\t\t\t\t// partialJson is only a streaming scratch buffer; never persist it.\n\t\t\t\tdelete (block as { partialJson?: string }).partialJson;\n\t\t\t}\n\t\t\toutput.stopReason = options?.signal?.aborted ? \"aborted\" : \"error\";\n\t\t\toutput.errorMessage = formatOpenAIResponsesError(error);\n\t\t\tstream.push({ type: \"error\", reason: output.stopReason, error: output });\n\t\t\tstream.end();\n\t\t}\n\t})();\n\n\treturn stream;\n};\n\nexport const streamSimpleOpenAIResponses: StreamFunction<\"openai-responses\", SimpleStreamOptions> = (\n\tmodel: Model<\"openai-responses\">,\n\tcontext: Context,\n\toptions?: SimpleStreamOptions,\n): AssistantMessageEventStream => {\n\tconst apiKey = options?.apiKey;\n\tif (!apiKey) {\n\t\tthrow new Error(`No API key for provider: ${model.provider}`);\n\t}\n\n\tconst base = buildBaseOptions(model, options, apiKey);\n\tconst requestedReasoning = options?.reasoning ?? model.defaultThinkingLevel;\n\tconst clampedReasoning = requestedReasoning ? clampThinkingLevel(model, requestedReasoning) : undefined;\n\tconst reasoningEffort = clampedReasoning === \"off\" ? \"none\" : clampedReasoning;\n\n\treturn streamOpenAIResponses(model, context, {\n\t\t...base,\n\t\treasoningEffort,\n\t} satisfies OpenAIResponsesOptions);\n};\n\nfunction resolveFuguBaseUrl(): string | undefined {\n\tif (typeof process === \"undefined\") return undefined;\n\tconst value = process.env.FUGU_BASE_URL?.trim();\n\tif (!value) return undefined;\n\tconst baseUrl = value.replace(/\\/+$/, \"\");\n\treturn baseUrl.endsWith(\"/v1\") ? baseUrl : `${baseUrl}/v1`;\n}\n\nfunction resolveBaseUrl(model: Model<\"openai-responses\">): string {\n\tif (isCloudflareProvider(model.provider)) return resolveCloudflareBaseUrl(model);\n\tif (model.provider === \"fugu\") return resolveFuguBaseUrl() ?? model.baseUrl;\n\treturn model.baseUrl;\n}\n\nfunction createClient(\n\tmodel: Model<\"openai-responses\">,\n\tcontext: Context,\n\tapiKey: string,\n\toptionsHeaders?: Record<string, string>,\n\tsessionId?: string,\n) {\n\tconst compat = getCompat(model);\n\tconst headers = { ...model.headers };\n\tif (model.provider === \"github-copilot\") {\n\t\tconst hasImages = hasCopilotVisionInput(context.messages);\n\t\tconst copilotHeaders = buildCopilotDynamicHeaders({\n\t\t\tmessages: context.messages,\n\t\t\thasImages,\n\t\t});\n\t\tObject.assign(headers, copilotHeaders);\n\t}\n\n\tif (sessionId) {\n\t\tif (compat.sendSessionIdHeader) {\n\t\t\theaders.session_id = sessionId;\n\t\t}\n\t\theaders[\"x-client-request-id\"] = sessionId;\n\t}\n\n\t// Merge options headers last so they can override defaults\n\tif (optionsHeaders) {\n\t\tObject.assign(headers, optionsHeaders);\n\t}\n\n\tconst defaultHeaders =\n\t\tmodel.provider === \"cloudflare-ai-gateway\"\n\t\t\t? {\n\t\t\t\t\t...headers,\n\t\t\t\t\tAuthorization: headers.Authorization ?? null,\n\t\t\t\t\t\"cf-aig-authorization\": `Bearer ${apiKey}`,\n\t\t\t\t}\n\t\t\t: headers;\n\n\treturn new OpenAI({\n\t\tapiKey,\n\t\tbaseURL: resolveBaseUrl(model),\n\t\tdangerouslyAllowBrowser: true,\n\t\tdefaultHeaders,\n\t});\n}\n\nfunction buildParams(model: Model<\"openai-responses\">, context: Context, options?: OpenAIResponsesOptions) {\n\tconst toolNameMap = createOpenAIResponsesToolNameMap(context.tools ?? []);\n\tconst messages = convertResponsesMessages(model, context, OPENAI_TOOL_CALL_PROVIDERS, { toolNameMap });\n\n\tconst cacheRetention = resolveCacheRetention(options?.cacheRetention);\n\tconst compat = getCompat(model);\n\tconst isGpt56 = model.provider === \"openai\" && model.id.startsWith(\"gpt-5.6\");\n\tconst params: ResponseCreateParamsStreaming = {\n\t\tmodel: model.id,\n\t\tinstructions: buildResponsesInstructions(context),\n\t\tinput: messages,\n\t\tstream: true,\n\t\tprompt_cache_key: cacheRetention === \"none\" ? undefined : clampOpenAIPromptCacheKey(options?.sessionId),\n\t\t...(cacheRetention !== \"none\" && isGpt56\n\t\t\t? { prompt_cache_options: options?.promptCacheOptions ?? { mode: \"implicit\" as const, ttl: \"30m\" as const } }\n\t\t\t: {}),\n\t\t...(isGpt56 ? {} : { prompt_cache_retention: getPromptCacheRetention(compat, cacheRetention) }),\n\t\tstore: false,\n\t};\n\tconst safetyIdentifier = options?.metadata?.safety_identifier;\n\tif (typeof safetyIdentifier === \"string\" && safetyIdentifier.length > 0) {\n\t\tparams.safety_identifier = safetyIdentifier;\n\t}\n\n\tif (options?.maxTokens) {\n\t\tparams.max_output_tokens = options?.maxTokens;\n\t}\n\n\tif (options?.temperature !== undefined) {\n\t\tparams.temperature = options?.temperature;\n\t}\n\n\tif (options?.serviceTier !== undefined && model.provider !== \"fugu\") {\n\t\tparams.service_tier = options.serviceTier;\n\t}\n\n\tif (context.tools && context.tools.length > 0) {\n\t\tparams.tools = convertResponsesTools(context.tools, { toolNameMap });\n\t}\n\n\tif (model.reasoning) {\n\t\tif (\n\t\t\toptions?.reasoningEffort ||\n\t\t\toptions?.reasoningSummary !== undefined ||\n\t\t\toptions?.reasoningMode ||\n\t\t\toptions?.reasoningContext\n\t\t) {\n\t\t\tconst effort = options?.reasoningEffort\n\t\t\t\t? options.reasoningEffort === \"none\"\n\t\t\t\t\t? (model.thinkingLevelMap?.off ?? \"none\")\n\t\t\t\t\t: (model.thinkingLevelMap?.[options.reasoningEffort] ?? options.reasoningEffort)\n\t\t\t\t: \"medium\";\n\t\t\tconst reasoningDisabled = options?.reasoningEffort === \"none\";\n\t\t\tconst supportsReasoningSummary = model.provider !== \"fugu\" || model.id === \"fugu-ultra\";\n\t\t\tparams.reasoning = {\n\t\t\t\teffort: effort as NonNullable<typeof params.reasoning>[\"effort\"],\n\t\t\t\t...(supportsReasoningSummary && !reasoningDisabled && options?.reasoningSummary !== null\n\t\t\t\t\t? { summary: options?.reasoningSummary ?? \"auto\" }\n\t\t\t\t\t: {}),\n\t\t\t\t...(options?.reasoningMode ? { mode: options.reasoningMode } : {}),\n\t\t\t\t...(options?.reasoningContext ? { context: options.reasoningContext } : {}),\n\t\t\t};\n\t\t\tif (supportsReasoningSummary && !reasoningDisabled) {\n\t\t\t\tparams.include = [\"reasoning.encrypted_content\"];\n\t\t\t}\n\t\t} else if (model.provider !== \"github-copilot\" && model.thinkingLevelMap?.off !== null) {\n\t\t\tparams.reasoning = {\n\t\t\t\teffort: (model.thinkingLevelMap?.off ?? \"none\") as NonNullable<typeof params.reasoning>[\"effort\"],\n\t\t\t};\n\t\t}\n\t}\n\n\treturn params;\n}\n\nfunction getServiceTierCostMultiplier(\n\tmodel: Pick<Model<\"openai-responses\">, \"id\">,\n\tserviceTier: ResponseCreateParamsStreaming[\"service_tier\"] | undefined,\n): number {\n\tswitch (serviceTier) {\n\t\tcase \"flex\":\n\t\t\treturn 0.5;\n\t\tcase \"priority\":\n\t\t\treturn model.id === \"gpt-5.5\" ? 2.5 : 2;\n\t\tdefault:\n\t\t\treturn 1;\n\t}\n}\n\nfunction applyServiceTierPricing(\n\tusage: Usage,\n\tserviceTier: ResponseCreateParamsStreaming[\"service_tier\"] | undefined,\n\tmodel: Pick<Model<\"openai-responses\">, \"id\">,\n) {\n\tconst multiplier = getServiceTierCostMultiplier(model, serviceTier);\n\tif (multiplier === 1) return;\n\n\tusage.cost.input *= multiplier;\n\tusage.cost.output *= multiplier;\n\tusage.cost.cacheRead *= multiplier;\n\tusage.cost.cacheWrite *= multiplier;\n\tusage.cost.total = usage.cost.input + usage.cost.output + usage.cost.cacheRead + usage.cost.cacheWrite;\n}\n"]}
|
|
@@ -3,5 +3,10 @@ export interface ToolNameMap {
|
|
|
3
3
|
toProviderName(name: string): string;
|
|
4
4
|
toOriginalName(name: string): string;
|
|
5
5
|
}
|
|
6
|
-
export
|
|
6
|
+
export interface ToolNameMapOptions {
|
|
7
|
+
reservedNames?: ReadonlySet<string>;
|
|
8
|
+
reservedSuffix?: string;
|
|
9
|
+
normalizeName?: (sanitizedName: string) => string;
|
|
10
|
+
}
|
|
11
|
+
export declare function createToolNameMap(tools: readonly Tool[], options?: ToolNameMapOptions): ToolNameMap;
|
|
7
12
|
//# sourceMappingURL=tool-names.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-names.d.ts","sourceRoot":"","sources":["../../src/utils/tool-names.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAKxC,MAAM,WAAW,WAAW;IAC3B,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACrC,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CACrC;AA6BD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,GAAG,WAAW,
|
|
1
|
+
{"version":3,"file":"tool-names.d.ts","sourceRoot":"","sources":["../../src/utils/tool-names.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAKxC,MAAM,WAAW,WAAW;IAC3B,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACrC,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CACrC;AAED,MAAM,WAAW,kBAAkB;IAClC,aAAa,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACpC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,MAAM,CAAC;CAClD;AA6BD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,WAAW,CAgCnG","sourcesContent":["import type { Tool } from \"../types.ts\";\n\nconst VALID_TOOL_NAME = /^[a-zA-Z0-9_-]{1,64}$/;\nconst MAX_TOOL_NAME_LENGTH = 64;\n\nexport interface ToolNameMap {\n\ttoProviderName(name: string): string;\n\ttoOriginalName(name: string): string;\n}\n\nexport interface ToolNameMapOptions {\n\treservedNames?: ReadonlySet<string>;\n\treservedSuffix?: string;\n\tnormalizeName?: (sanitizedName: string) => string;\n}\n\nfunction baseSanitizedToolName(name: string): string {\n\tconst sanitized = name\n\t\t.replace(/[^a-zA-Z0-9_-]/g, \"_\")\n\t\t.replace(/^_+|_+$/g, \"\")\n\t\t.slice(0, MAX_TOOL_NAME_LENGTH);\n\treturn sanitized.length > 0 ? sanitized : \"tool\";\n}\n\nfunction uniqueToolName(base: string, usedNames: Set<string>, reservedNames?: ReadonlySet<string>): string {\n\tif (!usedNames.has(base) && !reservedNames?.has(base)) {\n\t\tusedNames.add(base);\n\t\treturn base;\n\t}\n\n\tlet index = 2;\n\twhile (true) {\n\t\tconst suffix = `_${index}`;\n\t\tconst prefix = base.slice(0, MAX_TOOL_NAME_LENGTH - suffix.length);\n\t\tconst candidate = `${prefix}${suffix}`;\n\t\tif (!usedNames.has(candidate) && !reservedNames?.has(candidate)) {\n\t\t\tusedNames.add(candidate);\n\t\t\treturn candidate;\n\t\t}\n\t\tindex++;\n\t}\n}\n\nexport function createToolNameMap(tools: readonly Tool[], options?: ToolNameMapOptions): ToolNameMap {\n\tconst originalToProvider = new Map<string, string>();\n\tconst providerToOriginal = new Map<string, string>();\n\tconst usedProviderNames = new Set<string>();\n\n\tconst allocate = (originalName: string): string => {\n\t\tconst existing = originalToProvider.get(originalName);\n\t\tif (existing) return existing;\n\t\tconst sanitizedBase = VALID_TOOL_NAME.test(originalName) ? originalName : baseSanitizedToolName(originalName);\n\t\tconst normalizedBase = baseSanitizedToolName(options?.normalizeName?.(sanitizedBase) ?? sanitizedBase);\n\t\tconst base = options?.reservedNames?.has(normalizedBase)\n\t\t\t? baseSanitizedToolName(`${normalizedBase}${options.reservedSuffix ?? \"_tool\"}`)\n\t\t\t: normalizedBase;\n\t\tconst providerName = uniqueToolName(base, usedProviderNames, options?.reservedNames);\n\t\toriginalToProvider.set(originalName, providerName);\n\t\tproviderToOriginal.set(providerName, originalName);\n\t\treturn providerName;\n\t};\n\n\tfor (const tool of tools) {\n\t\tif (originalToProvider.has(tool.name)) throw new TypeError(`Duplicate tool name '${tool.name}'.`);\n\t\tallocate(tool.name);\n\t}\n\n\treturn {\n\t\ttoProviderName(name: string): string {\n\t\t\treturn allocate(name);\n\t\t},\n\t\ttoOriginalName(name: string): string {\n\t\t\treturn providerToOriginal.get(name) ?? name;\n\t\t},\n\t};\n}\n"]}
|
package/dist/utils/tool-names.js
CHANGED
|
@@ -7,8 +7,8 @@ function baseSanitizedToolName(name) {
|
|
|
7
7
|
.slice(0, MAX_TOOL_NAME_LENGTH);
|
|
8
8
|
return sanitized.length > 0 ? sanitized : "tool";
|
|
9
9
|
}
|
|
10
|
-
function uniqueToolName(base, usedNames) {
|
|
11
|
-
if (!usedNames.has(base)) {
|
|
10
|
+
function uniqueToolName(base, usedNames, reservedNames) {
|
|
11
|
+
if (!usedNames.has(base) && !reservedNames?.has(base)) {
|
|
12
12
|
usedNames.add(base);
|
|
13
13
|
return base;
|
|
14
14
|
}
|
|
@@ -17,26 +17,39 @@ function uniqueToolName(base, usedNames) {
|
|
|
17
17
|
const suffix = `_${index}`;
|
|
18
18
|
const prefix = base.slice(0, MAX_TOOL_NAME_LENGTH - suffix.length);
|
|
19
19
|
const candidate = `${prefix}${suffix}`;
|
|
20
|
-
if (!usedNames.has(candidate)) {
|
|
20
|
+
if (!usedNames.has(candidate) && !reservedNames?.has(candidate)) {
|
|
21
21
|
usedNames.add(candidate);
|
|
22
22
|
return candidate;
|
|
23
23
|
}
|
|
24
24
|
index++;
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
-
export function createToolNameMap(tools) {
|
|
27
|
+
export function createToolNameMap(tools, options) {
|
|
28
28
|
const originalToProvider = new Map();
|
|
29
29
|
const providerToOriginal = new Map();
|
|
30
30
|
const usedProviderNames = new Set();
|
|
31
|
+
const allocate = (originalName) => {
|
|
32
|
+
const existing = originalToProvider.get(originalName);
|
|
33
|
+
if (existing)
|
|
34
|
+
return existing;
|
|
35
|
+
const sanitizedBase = VALID_TOOL_NAME.test(originalName) ? originalName : baseSanitizedToolName(originalName);
|
|
36
|
+
const normalizedBase = baseSanitizedToolName(options?.normalizeName?.(sanitizedBase) ?? sanitizedBase);
|
|
37
|
+
const base = options?.reservedNames?.has(normalizedBase)
|
|
38
|
+
? baseSanitizedToolName(`${normalizedBase}${options.reservedSuffix ?? "_tool"}`)
|
|
39
|
+
: normalizedBase;
|
|
40
|
+
const providerName = uniqueToolName(base, usedProviderNames, options?.reservedNames);
|
|
41
|
+
originalToProvider.set(originalName, providerName);
|
|
42
|
+
providerToOriginal.set(providerName, originalName);
|
|
43
|
+
return providerName;
|
|
44
|
+
};
|
|
31
45
|
for (const tool of tools) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
providerToOriginal.set(providerName, tool.name);
|
|
46
|
+
if (originalToProvider.has(tool.name))
|
|
47
|
+
throw new TypeError(`Duplicate tool name '${tool.name}'.`);
|
|
48
|
+
allocate(tool.name);
|
|
36
49
|
}
|
|
37
50
|
return {
|
|
38
51
|
toProviderName(name) {
|
|
39
|
-
return
|
|
52
|
+
return allocate(name);
|
|
40
53
|
},
|
|
41
54
|
toOriginalName(name) {
|
|
42
55
|
return providerToOriginal.get(name) ?? name;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-names.js","sourceRoot":"","sources":["../../src/utils/tool-names.ts"],"names":[],"mappings":"AAEA,MAAM,eAAe,GAAG,uBAAuB,CAAC;AAChD,MAAM,oBAAoB,GAAG,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"tool-names.js","sourceRoot":"","sources":["../../src/utils/tool-names.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Tool } from \"../types.ts\";\n\nconst VALID_TOOL_NAME = /^[a-zA-Z0-9_-]{1,64}$/;\nconst MAX_TOOL_NAME_LENGTH = 64;\n\nexport interface ToolNameMap {\n\ttoProviderName(name: string): string;\n\ttoOriginalName(name: string): string;\n}\n\nexport interface ToolNameMapOptions {\n\treservedNames?: ReadonlySet<string>;\n\treservedSuffix?: string;\n\tnormalizeName?: (sanitizedName: string) => string;\n}\n\nfunction baseSanitizedToolName(name: string): string {\n\tconst sanitized = name\n\t\t.replace(/[^a-zA-Z0-9_-]/g, \"_\")\n\t\t.replace(/^_+|_+$/g, \"\")\n\t\t.slice(0, MAX_TOOL_NAME_LENGTH);\n\treturn sanitized.length > 0 ? sanitized : \"tool\";\n}\n\nfunction uniqueToolName(base: string, usedNames: Set<string>, reservedNames?: ReadonlySet<string>): string {\n\tif (!usedNames.has(base) && !reservedNames?.has(base)) {\n\t\tusedNames.add(base);\n\t\treturn base;\n\t}\n\n\tlet index = 2;\n\twhile (true) {\n\t\tconst suffix = `_${index}`;\n\t\tconst prefix = base.slice(0, MAX_TOOL_NAME_LENGTH - suffix.length);\n\t\tconst candidate = `${prefix}${suffix}`;\n\t\tif (!usedNames.has(candidate) && !reservedNames?.has(candidate)) {\n\t\t\tusedNames.add(candidate);\n\t\t\treturn candidate;\n\t\t}\n\t\tindex++;\n\t}\n}\n\nexport function createToolNameMap(tools: readonly Tool[], options?: ToolNameMapOptions): ToolNameMap {\n\tconst originalToProvider = new Map<string, string>();\n\tconst providerToOriginal = new Map<string, string>();\n\tconst usedProviderNames = new Set<string>();\n\n\tconst allocate = (originalName: string): string => {\n\t\tconst existing = originalToProvider.get(originalName);\n\t\tif (existing) return existing;\n\t\tconst sanitizedBase = VALID_TOOL_NAME.test(originalName) ? originalName : baseSanitizedToolName(originalName);\n\t\tconst normalizedBase = baseSanitizedToolName(options?.normalizeName?.(sanitizedBase) ?? sanitizedBase);\n\t\tconst base = options?.reservedNames?.has(normalizedBase)\n\t\t\t? baseSanitizedToolName(`${normalizedBase}${options.reservedSuffix ?? \"_tool\"}`)\n\t\t\t: normalizedBase;\n\t\tconst providerName = uniqueToolName(base, usedProviderNames, options?.reservedNames);\n\t\toriginalToProvider.set(originalName, providerName);\n\t\tproviderToOriginal.set(providerName, originalName);\n\t\treturn providerName;\n\t};\n\n\tfor (const tool of tools) {\n\t\tif (originalToProvider.has(tool.name)) throw new TypeError(`Duplicate tool name '${tool.name}'.`);\n\t\tallocate(tool.name);\n\t}\n\n\treturn {\n\t\ttoProviderName(name: string): string {\n\t\t\treturn allocate(name);\n\t\t},\n\t\ttoOriginalName(name: string): string {\n\t\t\treturn providerToOriginal.get(name) ?? name;\n\t\t},\n\t};\n}\n"]}
|
|
@@ -7,6 +7,9 @@ export interface ParsedTextToolCalls {
|
|
|
7
7
|
failure?: TextToolProtocolParseFailure;
|
|
8
8
|
}
|
|
9
9
|
export type TextToolProtocolVariant = "tool-tag" | "tool-call" | "fenced-json" | "function-xml";
|
|
10
|
+
/** Ordered dialects used by protocol calibration, from most compact/native-like to broadest fallback. */
|
|
11
|
+
export declare const TEXT_TOOL_PROTOCOL_VARIANTS: readonly TextToolProtocolVariant[];
|
|
12
|
+
export declare function isTextToolProtocolVariant(value: unknown): value is TextToolProtocolVariant;
|
|
10
13
|
export interface TextToolProtocolOptions {
|
|
11
14
|
variant?: TextToolProtocolVariant;
|
|
12
15
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"text-protocol.d.ts","sourceRoot":"","sources":["../../../src/utils/tool-repair/text-protocol.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAErD,MAAM,MAAM,4BAA4B,GAAG,SAAS,GAAG,cAAc,GAAG,cAAc,GAAG,mBAAmB,CAAC;AAE7G,MAAM,WAAW,mBAAmB;IACnC,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,4BAA4B,CAAC;CACvC;AAED,MAAM,MAAM,uBAAuB,GAAG,UAAU,GAAG,WAAW,GAAG,aAAa,GAAG,cAAc,CAAC;AAEhG,MAAM,WAAW,uBAAuB;IACvC,OAAO,CAAC,EAAE,uBAAuB,CAAC;CAClC;AA6VD,wBAAgB,gCAAgC,CAC/C,MAAM,EAAE,OAAO,GAAG,uBAAuB,GAAG,SAAS,GACnD,uBAAuB,GAAG,SAAS,CAIrC;AAmBD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,uBAAuB,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKlH;AA8ID,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,MAAM,CAchH;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,IAAI,EAAE,GAAG,mBAAmB,CAmBjG","sourcesContent":["import type { Tool, ToolCall } from \"../../types.ts\";\n\nexport type TextToolProtocolParseFailure = \"overlap\" | \"unrecognized\" | \"unknown-tool\" | \"validation-failed\";\n\nexport interface ParsedTextToolCalls {\n\tcalls: ToolCall[];\n\ttext: string;\n\tattempted: boolean;\n\tfailure?: TextToolProtocolParseFailure;\n}\n\nexport type TextToolProtocolVariant = \"tool-tag\" | \"tool-call\" | \"fenced-json\" | \"function-xml\";\n\nexport interface TextToolProtocolOptions {\n\tvariant?: TextToolProtocolVariant;\n}\n\ntype EnvelopeKind = \"pi_call\" | \"tool_call\" | \"fenced_json\" | \"function_xml\";\n\ninterface EnvelopeMatch {\n\tkind: EnvelopeKind;\n\tstart: number;\n\tend: number;\n\tname?: string;\n\tbody: string;\n}\n\nconst DEFAULT_TEXT_TOOL_PROTOCOL_VARIANT: TextToolProtocolVariant = \"tool-tag\";\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction escapeAttribute(value: string): string {\n\treturn value.replace(/&/g, \"&\").replace(/\"/g, \""\").replace(/</g, \"<\").replace(/>/g, \">\");\n}\n\nfunction unescapeAttribute(value: string): string {\n\treturn value\n\t\t.replace(/"/g, '\"')\n\t\t.replace(/'/g, \"'\")\n\t\t.replace(/</g, \"<\")\n\t\t.replace(/>/g, \">\")\n\t\t.replace(/&/g, \"&\");\n}\n\nfunction escapeXmlText(value: string): string {\n\treturn value.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\n}\n\nfunction unescapeXmlText(value: string): string {\n\treturn value\n\t\t.replace(/</g, \"<\")\n\t\t.replace(/>/g, \">\")\n\t\t.replace(/'/g, \"'\")\n\t\t.replace(/"/g, '\"')\n\t\t.replace(/&/g, \"&\");\n}\n\nfunction knownToolNames(tools: readonly Tool[]): Set<string> {\n\treturn new Set(tools.map((tool) => tool.name));\n}\n\nfunction findJsonObjectEnd(text: string, start: number): number | undefined {\n\tlet index = start;\n\twhile (/\\s/.test(text[index] ?? \"\")) index++;\n\tif (text[index] !== \"{\") return undefined;\n\tlet depth = 0;\n\tlet inString = false;\n\tlet escaped = false;\n\tfor (; index < text.length; index++) {\n\t\tconst char = text[index];\n\t\tif (inString) {\n\t\t\tif (escaped) {\n\t\t\t\tescaped = false;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (char === \"\\\\\") {\n\t\t\t\tescaped = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (char === '\"') inString = false;\n\t\t\tcontinue;\n\t\t}\n\t\tif (char === '\"') {\n\t\t\tinString = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (char === \"{\") depth++;\n\t\tif (char === \"}\") {\n\t\t\tdepth--;\n\t\t\tif (depth === 0) return index + 1;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nfunction isInsideMatch(index: number, matches: readonly EnvelopeMatch[]): boolean {\n\treturn matches.some((match) => index >= match.start && index < match.end);\n}\n\nfunction findToolEnvelopes(text: string): EnvelopeMatch[] {\n\tconst matches: EnvelopeMatch[] = [];\n\tconst openPiCallEnvelope = /<pi:call\\s+name=([\"'])(.*?)\\1\\s*>/g;\n\tfor (const match of text.matchAll(openPiCallEnvelope)) {\n\t\tif (isInsideMatch(match.index, matches)) continue;\n\t\tconst bodyStart = match.index + match[0].length;\n\t\tconst jsonEnd = findJsonObjectEnd(text, bodyStart);\n\t\tconst closePattern = /<\\/pi:call\\s*>/g;\n\t\tclosePattern.lastIndex = jsonEnd ?? bodyStart;\n\t\tconst candidateClose = closePattern.exec(text);\n\t\tconst textBeforeClose = candidateClose ? text.slice(jsonEnd ?? bodyStart, candidateClose.index) : undefined;\n\t\tconst closeMatch =\n\t\t\tcandidateClose &&\n\t\t\t(jsonEnd !== undefined ? textBeforeClose?.trim() === \"\" : !/<pi:call\\b/.test(textBeforeClose ?? \"\"))\n\t\t\t\t? candidateClose\n\t\t\t\t: undefined;\n\t\tconst bodyEnd = closeMatch?.index ?? jsonEnd;\n\t\tif (bodyEnd === undefined) continue;\n\t\tmatches.push({\n\t\t\tkind: \"pi_call\",\n\t\t\tstart: match.index,\n\t\t\tend: closeMatch ? closeMatch.index + closeMatch[0].length : bodyEnd,\n\t\t\tname: unescapeAttribute(match[2] ?? \"\"),\n\t\t\tbody: text.slice(bodyStart, bodyEnd),\n\t\t});\n\t}\n\n\tconst toolCallEnvelope = /<tool_call>([\\s\\S]*?)<\\/tool_call>/g;\n\tfor (const match of text.matchAll(toolCallEnvelope)) {\n\t\tmatches.push({\n\t\t\tkind: \"tool_call\",\n\t\t\tstart: match.index,\n\t\t\tend: match.index + match[0].length,\n\t\t\tbody: match[1] ?? \"\",\n\t\t});\n\t}\n\n\tconst fence = /```(?:tool|tool_call|json)\\s*\\n([\\s\\S]*?)\\n?```/gi;\n\tfor (const match of text.matchAll(fence)) {\n\t\tmatches.push({\n\t\t\tkind: \"fenced_json\",\n\t\t\tstart: match.index,\n\t\t\tend: match.index + match[0].length,\n\t\t\tbody: match[1] ?? \"\",\n\t\t});\n\t}\n\n\tconst functionEnvelope = /<function\\s+name=([\"'])(.*?)\\1\\s*>([\\s\\S]*?)<\\/function\\s*>/g;\n\tfor (const match of text.matchAll(functionEnvelope)) {\n\t\tmatches.push({\n\t\t\tkind: \"function_xml\",\n\t\t\tstart: match.index,\n\t\t\tend: match.index + match[0].length,\n\t\t\tname: unescapeAttribute(match[2] ?? \"\"),\n\t\t\tbody: match[3] ?? \"\",\n\t\t});\n\t}\n\n\treturn matches.sort((a, b) => a.start - b.start);\n}\n\nfunction hasOverlap(matches: readonly EnvelopeMatch[]): boolean {\n\tlet previousEnd = -1;\n\tfor (const match of matches) {\n\t\tif (match.start < previousEnd) return true;\n\t\tpreviousEnd = match.end;\n\t}\n\treturn false;\n}\n\ninterface ParsedMatch {\n\tmatch: EnvelopeMatch;\n\tcall: ToolCall;\n}\n\n/**\n * Parallel-call parity: a batch is never discarded wholesale over one overlap. Selects the\n * MAXIMUM-cardinality set of non-overlapping, successfully-parsed envelopes - the classic\n * interval-scheduling-maximization greedy (sort by end, keep whatever doesn't conflict with what's\n * already kept). This is deterministic and optimal, not a heuristic guess: when two envelopes\n * genuinely overlap (one nested in a malformed wrapper, most often), the one that ends first is\n * the one that leaves room for the most other calls, so it wins; the loser's byte range is simply\n * never consumed and its text survives as prose via {@link remainingText}.\n */\nfunction selectNonOverlappingParsedMatches(entries: readonly ParsedMatch[]): ParsedMatch[] {\n\tconst byEndThenStart = [...entries].sort((a, b) => a.match.end - b.match.end || a.match.start - b.match.start);\n\tconst kept: ParsedMatch[] = [];\n\tlet previousEnd = -1;\n\tfor (const entry of byEndThenStart) {\n\t\tif (entry.match.start < previousEnd) continue;\n\t\tkept.push(entry);\n\t\tpreviousEnd = entry.match.end;\n\t}\n\treturn kept.sort((a, b) => a.match.start - b.match.start);\n}\n\nfunction remainingText(text: string, matches: readonly EnvelopeMatch[]): string {\n\tlet cursor = 0;\n\tconst pieces: string[] = [];\n\tfor (const match of matches) {\n\t\tpieces.push(text.slice(cursor, match.start));\n\t\tcursor = match.end;\n\t}\n\tpieces.push(text.slice(cursor));\n\treturn pieces.join(\"\");\n}\n\nfunction coerceArguments(value: unknown): {\n\targuments: Record<string, unknown>;\n\trawArguments?: Record<string, unknown>;\n} {\n\tif (isRecord(value)) return { arguments: value };\n\treturn { arguments: value as Record<string, unknown>, rawArguments: { value } };\n}\n\nfunction normalizeSingleQuotedJson(raw: string): string | undefined {\n\tconst trimmed = raw.trim();\n\tif (!/^\\{[\\s\\S]*\\}$/.test(trimmed)) return undefined;\n\tif (!/'[^'\\\\]*(?:\\\\.[^'\\\\]*)*'/.test(trimmed)) return undefined;\n\treturn trimmed.replace(/'([^'\\\\]*(?:\\\\.[^'\\\\]*)*)'/g, (_match, inner: string) => {\n\t\treturn JSON.stringify(inner.replace(/\\\\'/g, \"'\"));\n\t});\n}\n\nfunction normalizeBareJsonObject(raw: string): string | undefined {\n\tconst trimmed = raw.trim();\n\tif (!/^\\{[\\s\\S]*\\}$/.test(trimmed)) return undefined;\n\tlet changed = false;\n\tlet normalized = trimmed.replace(\n\t\t/([{,]\\s*)([A-Za-z_][A-Za-z0-9_-]*)(\\s*:)/g,\n\t\t(_match, prefix: string, key: string, suffix: string) => {\n\t\t\tchanged = true;\n\t\t\treturn `${prefix}${JSON.stringify(key)}${suffix}`;\n\t\t},\n\t);\n\tnormalized = normalized.replace(/:\\s*([A-Za-z_./-][A-Za-z0-9_./:-]*)(?=\\s*[,}])/g, (_match, value: string) => {\n\t\tif ([\"true\", \"false\", \"null\"].includes(value)) return `:${value}`;\n\t\tchanged = true;\n\t\treturn `:${JSON.stringify(value)}`;\n\t});\n\treturn changed ? normalized : undefined;\n}\n\nfunction normalizedJsonCandidates(raw: string): string[] {\n\tconst candidates: string[] = [];\n\tconst objectEnd = findJsonObjectEnd(raw, 0);\n\tif (objectEnd !== undefined && raw.slice(objectEnd).trim()) candidates.push(raw.slice(0, objectEnd));\n\tconst singleQuoted = normalizeSingleQuotedJson(raw);\n\tif (singleQuoted) candidates.push(singleQuoted);\n\tconst bare = normalizeBareJsonObject(raw);\n\tif (bare) candidates.push(bare);\n\tconst singleQuotedBare = singleQuoted ? normalizeBareJsonObject(singleQuoted) : undefined;\n\tif (singleQuotedBare) candidates.push(singleQuotedBare);\n\treturn candidates;\n}\n\nfunction parseJsonValue(raw: string): { ok: true; value: unknown } | { ok: false } {\n\ttry {\n\t\treturn { ok: true, value: JSON.parse(raw) as unknown };\n\t} catch {\n\t\tfor (const normalized of normalizedJsonCandidates(raw)) {\n\t\t\ttry {\n\t\t\t\treturn { ok: true, value: JSON.parse(normalized) as unknown };\n\t\t\t} catch {\n\t\t\t\t// Try the next bounded normalization candidate.\n\t\t\t}\n\t\t}\n\t\treturn { ok: false };\n\t}\n}\n\nfunction parseJsonObject(raw: string): Record<string, unknown> | undefined {\n\tconst parsed = parseJsonValue(raw);\n\treturn parsed.ok && isRecord(parsed.value) ? parsed.value : undefined;\n}\n\nfunction textToolErrorMessage(name: string, names: readonly string[]): string | undefined {\n\tif (names.includes(name)) return undefined;\n\treturn `Unknown tool \"${name}\". Valid tools: ${names.join(\", \")}.`;\n}\n\nfunction extractNameFromMalformedJson(raw: string): string | undefined {\n\tconst match = /\"(?:name|tool)\"\\s*:\\s*\"([^\"]+)\"/.exec(raw);\n\treturn match?.[1];\n}\n\nfunction parsePiCallEnvelope(match: EnvelopeMatch, names: readonly string[], index: number): ToolCall | undefined {\n\tif (!match.name) return undefined;\n\tconst parsed = parseJsonValue(match.body);\n\tconst args = parsed.ok ? coerceArguments(parsed.value) : coerceArguments(match.body.trim());\n\treturn {\n\t\ttype: \"toolCall\",\n\t\tid: `text-tool-${index}`,\n\t\tname: match.name,\n\t\targuments: args.arguments,\n\t\trawArguments: parsed.ok ? args.rawArguments : { text: match.body.trim() },\n\t\tsource: \"text-protocol\",\n\t\terrorMessage: textToolErrorMessage(match.name, names),\n\t};\n}\n\nfunction parseFunctionXmlEnvelope(match: EnvelopeMatch, names: readonly string[], index: number): ToolCall | undefined {\n\tif (!match.name) return undefined;\n\tif (/<\\/?function\\b/i.test(match.body)) return undefined;\n\tconst params: Record<string, string> = {};\n\tlet cursor = 0;\n\tlet sawParam = false;\n\tconst paramPattern = /<param\\s+name=([\"'])(.*?)\\1\\s*>([\\s\\S]*?)<\\/param\\s*>/g;\n\tfor (const param of match.body.matchAll(paramPattern)) {\n\t\tif (match.body.slice(cursor, param.index).trim()) return undefined;\n\t\tconst name = unescapeAttribute(param[2] ?? \"\");\n\t\tif (!name || name in params) return undefined;\n\t\tparams[name] = unescapeXmlText((param[3] ?? \"\").trim());\n\t\tcursor = param.index + param[0].length;\n\t\tsawParam = true;\n\t}\n\tif (match.body.slice(cursor).trim()) return undefined;\n\tif (!sawParam) return undefined;\n\treturn {\n\t\ttype: \"toolCall\",\n\t\tid: `text-tool-${index}`,\n\t\tname: match.name,\n\t\targuments: params,\n\t\tsource: \"text-protocol\",\n\t\terrorMessage: textToolErrorMessage(match.name, names),\n\t};\n}\n\nfunction parseEnvelope(match: EnvelopeMatch, names: readonly string[], index: number): ToolCall | undefined {\n\tif (match.kind === \"pi_call\") return parsePiCallEnvelope(match, names, index);\n\tif (match.kind === \"function_xml\") return parseFunctionXmlEnvelope(match, names, index);\n\n\tconst parsed = parseJsonObject(match.body);\n\tif (!parsed) {\n\t\tconst name = extractNameFromMalformedJson(match.body);\n\t\tif (!name) return undefined;\n\t\treturn {\n\t\t\ttype: \"toolCall\",\n\t\t\tid: `text-tool-${index}`,\n\t\t\tname,\n\t\t\targuments: match.body.trim() as unknown as Record<string, unknown>,\n\t\t\trawArguments: { text: match.body.trim() },\n\t\t\tsource: \"text-protocol\",\n\t\t\terrorMessage: textToolErrorMessage(name, names),\n\t\t};\n\t}\n\n\tconst wrappedTool = isRecord(parsed.tool) ? parsed.tool : undefined;\n\tconst nameValue = parsed.name ?? (typeof parsed.tool === \"string\" ? parsed.tool : undefined) ?? wrappedTool?.name;\n\tif (typeof nameValue !== \"string\") return undefined;\n\tconst argsValue = parsed.arguments ?? parsed.args ?? wrappedTool?.arguments ?? wrappedTool?.args ?? {};\n\tconst args = coerceArguments(argsValue);\n\treturn {\n\t\ttype: \"toolCall\",\n\t\tid: `text-tool-${index}`,\n\t\tname: nameValue,\n\t\targuments: args.arguments,\n\t\trawArguments: args.rawArguments,\n\t\tsource: \"text-protocol\",\n\t\terrorMessage: textToolErrorMessage(nameValue, names),\n\t};\n}\n\nexport function normalizeTextToolProtocolOptions(\n\toption: boolean | TextToolProtocolOptions | undefined,\n): TextToolProtocolOptions | undefined {\n\tif (!option) return undefined;\n\tif (option === true) return { variant: DEFAULT_TEXT_TOOL_PROTOCOL_VARIANT };\n\treturn { variant: option.variant ?? DEFAULT_TEXT_TOOL_PROTOCOL_VARIANT };\n}\n\nfunction functionXmlParamValue(value: unknown): string {\n\tif (typeof value === \"string\") return value;\n\tconst json = JSON.stringify(value);\n\treturn json ?? String(value);\n}\n\nfunction formatFunctionXmlEnvelope(toolName: string, argsJson: string): string {\n\tconst args = parseJsonObject(argsJson) ?? {};\n\tconst params = Object.entries(args)\n\t\t.map(\n\t\t\t([name, value]) =>\n\t\t\t\t`<param name=\"${escapeAttribute(name)}\">${escapeXmlText(functionXmlParamValue(value))}</param>`,\n\t\t)\n\t\t.join(\"\");\n\treturn `<function name=\"${escapeAttribute(toolName)}\">${params}</function>`;\n}\n\n/**\n * Renders one call as the wire text for `variant`. Exported read-only for the history round-trip\n * render: it reuses this so a prior text-protocol call is echoed back to the model in the\n * exact dialect the primer taught it, instead of a second parallel formatting path.\n */\nexport function formatVariantEnvelope(variant: TextToolProtocolVariant, toolName: string, argsJson: string): string {\n\tif (variant === \"tool-call\") return `<tool_call>{\"name\":\"${toolName}\",\"arguments\":${argsJson}}</tool_call>`;\n\tif (variant === \"fenced-json\") return `\\`\\`\\`tool_call\\n{\"name\":\"${toolName}\",\"arguments\":${argsJson}}\\n\\`\\`\\``;\n\tif (variant === \"function-xml\") return formatFunctionXmlEnvelope(toolName, argsJson);\n\treturn `<pi:call name=\"${escapeAttribute(toolName)}\">${argsJson}</pi:call>`;\n}\n\nfunction schemaRecord(value: unknown): Record<string, unknown> | undefined {\n\treturn isRecord(value) ? value : undefined;\n}\n\nfunction firstString(value: unknown): string | undefined {\n\treturn typeof value === \"string\" ? value : undefined;\n}\n\nfunction schemaType(schema: Record<string, unknown>): string | undefined {\n\tconst type = schema.type;\n\tif (typeof type === \"string\") return type;\n\tif (!Array.isArray(type)) return undefined;\n\treturn type.filter(firstString).find((entry) => entry !== \"null\");\n}\n\nfunction stringExampleForProperty(propertyName: string | undefined): string {\n\tif (propertyName === \"path\") return \"src/index.ts\";\n\tif (propertyName === \"command\") return \"echo ok\";\n\tif (propertyName === \"oldText\") return \"foo\";\n\tif (propertyName === \"newText\") return \"bar\";\n\tif (propertyName === \"content\") return \"text\";\n\treturn \"value\";\n}\n\nfunction exampleValueForSchema(schemaValue: unknown, propertyName?: string): unknown {\n\tconst schema = schemaRecord(schemaValue);\n\tif (!schema) return stringExampleForProperty(propertyName);\n\tconst constValue = schema.const;\n\tif (constValue !== undefined) return constValue;\n\tconst enumValues = Array.isArray(schema.enum) ? schema.enum : [];\n\tif (enumValues.length > 0) return enumValues[0];\n\tconst defaultValue = schema.default;\n\tif (defaultValue !== undefined) return defaultValue;\n\tconst type = schemaType(schema);\n\tif (type === \"number\" || type === \"integer\") return 1;\n\tif (type === \"boolean\") return true;\n\tif (type === \"array\") return [exampleValueForSchema(schema.items, propertyName)];\n\tif (type === \"object\") return exampleArgumentsForParameters(schema);\n\treturn stringExampleForProperty(propertyName);\n}\n\nfunction requiredPropertyNames(parameters: Record<string, unknown> | undefined): string[] {\n\treturn Array.isArray(parameters?.required) ? parameters.required.filter(firstString) : [];\n}\n\nfunction exampleArgumentsForParameters(parametersValue: unknown): Record<string, unknown> {\n\tconst parameters = schemaRecord(parametersValue);\n\tconst properties = schemaRecord(parameters?.properties);\n\tif (!properties) return {};\n\tconst args: Record<string, unknown> = {};\n\tfor (const name of requiredPropertyNames(parameters)) {\n\t\targs[name] = exampleValueForSchema(properties[name], name);\n\t}\n\treturn args;\n}\n\nfunction typeLabel(schemaValue: unknown): string {\n\tconst schema = schemaRecord(schemaValue);\n\tif (!schema) return \"string\";\n\tconst enumValues = Array.isArray(schema.enum) ? schema.enum : [];\n\tif (enumValues.length > 0 && enumValues.every((entry) => [\"string\", \"number\", \"boolean\"].includes(typeof entry))) {\n\t\treturn enumValues.map(String).join(\"|\");\n\t}\n\tconst type = schemaType(schema);\n\tif (type === \"integer\" || type === \"number\") return \"number\";\n\tif (type === \"boolean\") return \"bool\";\n\tif (type === \"array\") return `${typeLabel(schema.items)}[]`;\n\tif (type === \"object\") {\n\t\tconst properties = schemaRecord(schema.properties);\n\t\tif (!properties) return \"{}\";\n\t\tconst entries = Object.entries(properties)\n\t\t\t.slice(0, 4)\n\t\t\t.map(([name, value]) => `${name}:${typeLabel(value)}`);\n\t\tconst suffix = Object.keys(properties).length > entries.length ? \",...\" : \"\";\n\t\treturn `{${entries.join(\",\")}${suffix}}`;\n\t}\n\treturn \"string\";\n}\n\nfunction orderedPropertyNames(properties: Record<string, unknown>, required: readonly string[]): string[] {\n\tconst requiredSet = new Set(required);\n\treturn [\n\t\t...required.filter((name) => name in properties),\n\t\t...Object.keys(properties).filter((name) => !requiredSet.has(name)),\n\t];\n}\n\nfunction formatDefault(value: unknown): string {\n\tconst json = JSON.stringify(value);\n\treturn json ? `=${json}` : \"\";\n}\n\nfunction formatToolProjection(tool: Tool): string {\n\tconst parameters = schemaRecord(tool.parameters);\n\tconst properties = schemaRecord(parameters?.properties);\n\tconst required = requiredPropertyNames(parameters);\n\tconst requiredSet = new Set(required);\n\tconst args = properties\n\t\t? orderedPropertyNames(properties, required)\n\t\t\t\t.map((name) => {\n\t\t\t\t\tconst schema = schemaRecord(properties[name]);\n\t\t\t\t\tconst optional = requiredSet.has(name) ? \"\" : \"?\";\n\t\t\t\t\tconst defaultText = schema && \"default\" in schema ? formatDefault(schema.default) : \"\";\n\t\t\t\t\treturn `${name}:${typeLabel(schema)}${optional}${defaultText}`;\n\t\t\t\t})\n\t\t\t\t.join(\", \")\n\t\t: \"\";\n\tconst description = tool.description.replace(/\\s+/g, \" \").trim();\n\treturn `${tool.name}(${args}) - ${description}`;\n}\n\nfunction toolHasArrayParameter(tool: Tool): boolean {\n\tconst parameters = schemaRecord(tool.parameters);\n\tconst properties = schemaRecord(parameters?.properties);\n\tif (!properties) return false;\n\treturn Object.values(properties).some((schemaValue) => schemaType(schemaRecord(schemaValue) ?? {}) === \"array\");\n}\n\nfunction exampleTools(tools: readonly Tool[]): Tool[] {\n\tconst examples: Tool[] = [];\n\tconst readTool = tools.find((tool) => tool.name === \"read\");\n\tif (readTool) examples.push(readTool);\n\tif (examples.length === 0) examples.push(tools[0]);\n\tconst editTool = tools.find((tool) => tool.name === \"edit\" && !examples.includes(tool));\n\tconst arrayTool = editTool ?? tools.find((tool) => !examples.includes(tool) && toolHasArrayParameter(tool));\n\tif (arrayTool) examples.push(arrayTool);\n\treturn examples;\n}\n\nfunction protocolHeader(variant: TextToolProtocolVariant): string[] {\n\treturn [\n\t\t\"Text tool-call protocol is enabled. To call a tool, emit an envelope in exactly this shape:\",\n\t\tformatVariantEnvelope(variant, \"TOOL\", '{\"arg\":\"value\"}'),\n\t\t\"Arguments are one JSON object: double-quoted keys/strings, arrays as [ ] (never a quoted string), optional args omitted rather than null.\",\n\t\t\"Reasoning may appear as prose before an envelope, never inside one.\",\n\t\t\"Emit several envelopes in one reply to call multiple tools in parallel; each is executed and answered on its own.\",\n\t\t\"Files, directories, searches, edits, writes, and shell commands always go through a tool envelope - never type out shell commands, file paths, or invented results yourself.\",\n\t];\n}\n\nexport function generateTextToolProtocolPrimer(tools: readonly Tool[], options?: TextToolProtocolOptions): string {\n\tif (tools.length === 0) return \"\";\n\tconst variant = options?.variant ?? DEFAULT_TEXT_TOOL_PROTOCOL_VARIANT;\n\tconst lines = [...protocolHeader(variant), \"Examples:\"];\n\tfor (const tool of exampleTools(tools)) {\n\t\tlines.push(\n\t\t\tformatVariantEnvelope(variant, tool.name, JSON.stringify(exampleArgumentsForParameters(tool.parameters))),\n\t\t);\n\t}\n\tlines.push(\"Available tools:\");\n\tfor (const tool of tools) {\n\t\tlines.push(formatToolProjection(tool));\n\t}\n\treturn lines.join(\"\\n\");\n}\n\nexport function parseTextToolCalls(text: string, knownTools: readonly Tool[]): ParsedTextToolCalls {\n\tif (knownTools.length === 0) return { calls: [], text, attempted: false };\n\tconst matches = findToolEnvelopes(text);\n\tif (matches.length === 0) return { calls: [], text, attempted: false };\n\tconst names = [...knownToolNames(knownTools)];\n\tconst parsed: ParsedMatch[] = [];\n\tfor (const [index, match] of matches.entries()) {\n\t\tconst call = parseEnvelope(match, names, index + 1);\n\t\tif (call) parsed.push({ match, call });\n\t}\n\tconst selected = selectNonOverlappingParsedMatches(parsed);\n\tif (selected.length === 0) {\n\t\treturn { calls: [], text, attempted: true, failure: hasOverlap(matches) ? \"overlap\" : \"unrecognized\" };\n\t}\n\tconst remainder = remainingText(\n\t\ttext,\n\t\tselected.map((entry) => entry.match),\n\t);\n\treturn { calls: selected.map((entry) => entry.call), text: remainder.trim(), attempted: true };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"text-protocol.d.ts","sourceRoot":"","sources":["../../../src/utils/tool-repair/text-protocol.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAErD,MAAM,MAAM,4BAA4B,GAAG,SAAS,GAAG,cAAc,GAAG,cAAc,GAAG,mBAAmB,CAAC;AAE7G,MAAM,WAAW,mBAAmB;IACnC,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,4BAA4B,CAAC;CACvC;AAED,MAAM,MAAM,uBAAuB,GAAG,UAAU,GAAG,WAAW,GAAG,aAAa,GAAG,cAAc,CAAC;AAEhG,yGAAyG;AACzG,eAAO,MAAM,2BAA2B,EAAE,SAAS,uBAAuB,EAKzE,CAAC;AAEF,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,uBAAuB,CAE1F;AAED,MAAM,WAAW,uBAAuB;IACvC,OAAO,CAAC,EAAE,uBAAuB,CAAC;CAClC;AA6VD,wBAAgB,gCAAgC,CAC/C,MAAM,EAAE,OAAO,GAAG,uBAAuB,GAAG,SAAS,GACnD,uBAAuB,GAAG,SAAS,CAIrC;AAmBD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,uBAAuB,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAKlH;AA8ID,wBAAgB,8BAA8B,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,MAAM,CAchH;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,IAAI,EAAE,GAAG,mBAAmB,CAmBjG","sourcesContent":["import type { Tool, ToolCall } from \"../../types.ts\";\n\nexport type TextToolProtocolParseFailure = \"overlap\" | \"unrecognized\" | \"unknown-tool\" | \"validation-failed\";\n\nexport interface ParsedTextToolCalls {\n\tcalls: ToolCall[];\n\ttext: string;\n\tattempted: boolean;\n\tfailure?: TextToolProtocolParseFailure;\n}\n\nexport type TextToolProtocolVariant = \"tool-tag\" | \"tool-call\" | \"fenced-json\" | \"function-xml\";\n\n/** Ordered dialects used by protocol calibration, from most compact/native-like to broadest fallback. */\nexport const TEXT_TOOL_PROTOCOL_VARIANTS: readonly TextToolProtocolVariant[] = [\n\t\"tool-tag\",\n\t\"tool-call\",\n\t\"fenced-json\",\n\t\"function-xml\",\n];\n\nexport function isTextToolProtocolVariant(value: unknown): value is TextToolProtocolVariant {\n\treturn typeof value === \"string\" && TEXT_TOOL_PROTOCOL_VARIANTS.includes(value as TextToolProtocolVariant);\n}\n\nexport interface TextToolProtocolOptions {\n\tvariant?: TextToolProtocolVariant;\n}\n\ntype EnvelopeKind = \"pi_call\" | \"tool_call\" | \"fenced_json\" | \"function_xml\";\n\ninterface EnvelopeMatch {\n\tkind: EnvelopeKind;\n\tstart: number;\n\tend: number;\n\tname?: string;\n\tbody: string;\n}\n\nconst DEFAULT_TEXT_TOOL_PROTOCOL_VARIANT: TextToolProtocolVariant = \"tool-tag\";\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction escapeAttribute(value: string): string {\n\treturn value.replace(/&/g, \"&\").replace(/\"/g, \""\").replace(/</g, \"<\").replace(/>/g, \">\");\n}\n\nfunction unescapeAttribute(value: string): string {\n\treturn value\n\t\t.replace(/"/g, '\"')\n\t\t.replace(/'/g, \"'\")\n\t\t.replace(/</g, \"<\")\n\t\t.replace(/>/g, \">\")\n\t\t.replace(/&/g, \"&\");\n}\n\nfunction escapeXmlText(value: string): string {\n\treturn value.replace(/&/g, \"&\").replace(/</g, \"<\").replace(/>/g, \">\");\n}\n\nfunction unescapeXmlText(value: string): string {\n\treturn value\n\t\t.replace(/</g, \"<\")\n\t\t.replace(/>/g, \">\")\n\t\t.replace(/'/g, \"'\")\n\t\t.replace(/"/g, '\"')\n\t\t.replace(/&/g, \"&\");\n}\n\nfunction knownToolNames(tools: readonly Tool[]): Set<string> {\n\treturn new Set(tools.map((tool) => tool.name));\n}\n\nfunction findJsonObjectEnd(text: string, start: number): number | undefined {\n\tlet index = start;\n\twhile (/\\s/.test(text[index] ?? \"\")) index++;\n\tif (text[index] !== \"{\") return undefined;\n\tlet depth = 0;\n\tlet inString = false;\n\tlet escaped = false;\n\tfor (; index < text.length; index++) {\n\t\tconst char = text[index];\n\t\tif (inString) {\n\t\t\tif (escaped) {\n\t\t\t\tescaped = false;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (char === \"\\\\\") {\n\t\t\t\tescaped = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (char === '\"') inString = false;\n\t\t\tcontinue;\n\t\t}\n\t\tif (char === '\"') {\n\t\t\tinString = true;\n\t\t\tcontinue;\n\t\t}\n\t\tif (char === \"{\") depth++;\n\t\tif (char === \"}\") {\n\t\t\tdepth--;\n\t\t\tif (depth === 0) return index + 1;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nfunction isInsideMatch(index: number, matches: readonly EnvelopeMatch[]): boolean {\n\treturn matches.some((match) => index >= match.start && index < match.end);\n}\n\nfunction findToolEnvelopes(text: string): EnvelopeMatch[] {\n\tconst matches: EnvelopeMatch[] = [];\n\tconst openPiCallEnvelope = /<pi:call\\s+name=([\"'])(.*?)\\1\\s*>/g;\n\tfor (const match of text.matchAll(openPiCallEnvelope)) {\n\t\tif (isInsideMatch(match.index, matches)) continue;\n\t\tconst bodyStart = match.index + match[0].length;\n\t\tconst jsonEnd = findJsonObjectEnd(text, bodyStart);\n\t\tconst closePattern = /<\\/pi:call\\s*>/g;\n\t\tclosePattern.lastIndex = jsonEnd ?? bodyStart;\n\t\tconst candidateClose = closePattern.exec(text);\n\t\tconst textBeforeClose = candidateClose ? text.slice(jsonEnd ?? bodyStart, candidateClose.index) : undefined;\n\t\tconst closeMatch =\n\t\t\tcandidateClose &&\n\t\t\t(jsonEnd !== undefined ? textBeforeClose?.trim() === \"\" : !/<pi:call\\b/.test(textBeforeClose ?? \"\"))\n\t\t\t\t? candidateClose\n\t\t\t\t: undefined;\n\t\tconst bodyEnd = closeMatch?.index ?? jsonEnd;\n\t\tif (bodyEnd === undefined) continue;\n\t\tmatches.push({\n\t\t\tkind: \"pi_call\",\n\t\t\tstart: match.index,\n\t\t\tend: closeMatch ? closeMatch.index + closeMatch[0].length : bodyEnd,\n\t\t\tname: unescapeAttribute(match[2] ?? \"\"),\n\t\t\tbody: text.slice(bodyStart, bodyEnd),\n\t\t});\n\t}\n\n\tconst toolCallEnvelope = /<tool_call>([\\s\\S]*?)<\\/tool_call>/g;\n\tfor (const match of text.matchAll(toolCallEnvelope)) {\n\t\tmatches.push({\n\t\t\tkind: \"tool_call\",\n\t\t\tstart: match.index,\n\t\t\tend: match.index + match[0].length,\n\t\t\tbody: match[1] ?? \"\",\n\t\t});\n\t}\n\n\tconst fence = /```(?:tool|tool_call|json)\\s*\\n([\\s\\S]*?)\\n?```/gi;\n\tfor (const match of text.matchAll(fence)) {\n\t\tmatches.push({\n\t\t\tkind: \"fenced_json\",\n\t\t\tstart: match.index,\n\t\t\tend: match.index + match[0].length,\n\t\t\tbody: match[1] ?? \"\",\n\t\t});\n\t}\n\n\tconst functionEnvelope = /<function\\s+name=([\"'])(.*?)\\1\\s*>([\\s\\S]*?)<\\/function\\s*>/g;\n\tfor (const match of text.matchAll(functionEnvelope)) {\n\t\tmatches.push({\n\t\t\tkind: \"function_xml\",\n\t\t\tstart: match.index,\n\t\t\tend: match.index + match[0].length,\n\t\t\tname: unescapeAttribute(match[2] ?? \"\"),\n\t\t\tbody: match[3] ?? \"\",\n\t\t});\n\t}\n\n\treturn matches.sort((a, b) => a.start - b.start);\n}\n\nfunction hasOverlap(matches: readonly EnvelopeMatch[]): boolean {\n\tlet previousEnd = -1;\n\tfor (const match of matches) {\n\t\tif (match.start < previousEnd) return true;\n\t\tpreviousEnd = match.end;\n\t}\n\treturn false;\n}\n\ninterface ParsedMatch {\n\tmatch: EnvelopeMatch;\n\tcall: ToolCall;\n}\n\n/**\n * Parallel-call parity: a batch is never discarded wholesale over one overlap. Selects the\n * MAXIMUM-cardinality set of non-overlapping, successfully-parsed envelopes - the classic\n * interval-scheduling-maximization greedy (sort by end, keep whatever doesn't conflict with what's\n * already kept). This is deterministic and optimal, not a heuristic guess: when two envelopes\n * genuinely overlap (one nested in a malformed wrapper, most often), the one that ends first is\n * the one that leaves room for the most other calls, so it wins; the loser's byte range is simply\n * never consumed and its text survives as prose via {@link remainingText}.\n */\nfunction selectNonOverlappingParsedMatches(entries: readonly ParsedMatch[]): ParsedMatch[] {\n\tconst byEndThenStart = [...entries].sort((a, b) => a.match.end - b.match.end || a.match.start - b.match.start);\n\tconst kept: ParsedMatch[] = [];\n\tlet previousEnd = -1;\n\tfor (const entry of byEndThenStart) {\n\t\tif (entry.match.start < previousEnd) continue;\n\t\tkept.push(entry);\n\t\tpreviousEnd = entry.match.end;\n\t}\n\treturn kept.sort((a, b) => a.match.start - b.match.start);\n}\n\nfunction remainingText(text: string, matches: readonly EnvelopeMatch[]): string {\n\tlet cursor = 0;\n\tconst pieces: string[] = [];\n\tfor (const match of matches) {\n\t\tpieces.push(text.slice(cursor, match.start));\n\t\tcursor = match.end;\n\t}\n\tpieces.push(text.slice(cursor));\n\treturn pieces.join(\"\");\n}\n\nfunction coerceArguments(value: unknown): {\n\targuments: Record<string, unknown>;\n\trawArguments?: Record<string, unknown>;\n} {\n\tif (isRecord(value)) return { arguments: value };\n\treturn { arguments: value as Record<string, unknown>, rawArguments: { value } };\n}\n\nfunction normalizeSingleQuotedJson(raw: string): string | undefined {\n\tconst trimmed = raw.trim();\n\tif (!/^\\{[\\s\\S]*\\}$/.test(trimmed)) return undefined;\n\tif (!/'[^'\\\\]*(?:\\\\.[^'\\\\]*)*'/.test(trimmed)) return undefined;\n\treturn trimmed.replace(/'([^'\\\\]*(?:\\\\.[^'\\\\]*)*)'/g, (_match, inner: string) => {\n\t\treturn JSON.stringify(inner.replace(/\\\\'/g, \"'\"));\n\t});\n}\n\nfunction normalizeBareJsonObject(raw: string): string | undefined {\n\tconst trimmed = raw.trim();\n\tif (!/^\\{[\\s\\S]*\\}$/.test(trimmed)) return undefined;\n\tlet changed = false;\n\tlet normalized = trimmed.replace(\n\t\t/([{,]\\s*)([A-Za-z_][A-Za-z0-9_-]*)(\\s*:)/g,\n\t\t(_match, prefix: string, key: string, suffix: string) => {\n\t\t\tchanged = true;\n\t\t\treturn `${prefix}${JSON.stringify(key)}${suffix}`;\n\t\t},\n\t);\n\tnormalized = normalized.replace(/:\\s*([A-Za-z_./-][A-Za-z0-9_./:-]*)(?=\\s*[,}])/g, (_match, value: string) => {\n\t\tif ([\"true\", \"false\", \"null\"].includes(value)) return `:${value}`;\n\t\tchanged = true;\n\t\treturn `:${JSON.stringify(value)}`;\n\t});\n\treturn changed ? normalized : undefined;\n}\n\nfunction normalizedJsonCandidates(raw: string): string[] {\n\tconst candidates: string[] = [];\n\tconst objectEnd = findJsonObjectEnd(raw, 0);\n\tif (objectEnd !== undefined && raw.slice(objectEnd).trim()) candidates.push(raw.slice(0, objectEnd));\n\tconst singleQuoted = normalizeSingleQuotedJson(raw);\n\tif (singleQuoted) candidates.push(singleQuoted);\n\tconst bare = normalizeBareJsonObject(raw);\n\tif (bare) candidates.push(bare);\n\tconst singleQuotedBare = singleQuoted ? normalizeBareJsonObject(singleQuoted) : undefined;\n\tif (singleQuotedBare) candidates.push(singleQuotedBare);\n\treturn candidates;\n}\n\nfunction parseJsonValue(raw: string): { ok: true; value: unknown } | { ok: false } {\n\ttry {\n\t\treturn { ok: true, value: JSON.parse(raw) as unknown };\n\t} catch {\n\t\tfor (const normalized of normalizedJsonCandidates(raw)) {\n\t\t\ttry {\n\t\t\t\treturn { ok: true, value: JSON.parse(normalized) as unknown };\n\t\t\t} catch {\n\t\t\t\t// Try the next bounded normalization candidate.\n\t\t\t}\n\t\t}\n\t\treturn { ok: false };\n\t}\n}\n\nfunction parseJsonObject(raw: string): Record<string, unknown> | undefined {\n\tconst parsed = parseJsonValue(raw);\n\treturn parsed.ok && isRecord(parsed.value) ? parsed.value : undefined;\n}\n\nfunction textToolErrorMessage(name: string, names: readonly string[]): string | undefined {\n\tif (names.includes(name)) return undefined;\n\treturn `Unknown tool \"${name}\". Valid tools: ${names.join(\", \")}.`;\n}\n\nfunction extractNameFromMalformedJson(raw: string): string | undefined {\n\tconst match = /\"(?:name|tool)\"\\s*:\\s*\"([^\"]+)\"/.exec(raw);\n\treturn match?.[1];\n}\n\nfunction parsePiCallEnvelope(match: EnvelopeMatch, names: readonly string[], index: number): ToolCall | undefined {\n\tif (!match.name) return undefined;\n\tconst parsed = parseJsonValue(match.body);\n\tconst args = parsed.ok ? coerceArguments(parsed.value) : coerceArguments(match.body.trim());\n\treturn {\n\t\ttype: \"toolCall\",\n\t\tid: `text-tool-${index}`,\n\t\tname: match.name,\n\t\targuments: args.arguments,\n\t\trawArguments: parsed.ok ? args.rawArguments : { text: match.body.trim() },\n\t\tsource: \"text-protocol\",\n\t\terrorMessage: textToolErrorMessage(match.name, names),\n\t};\n}\n\nfunction parseFunctionXmlEnvelope(match: EnvelopeMatch, names: readonly string[], index: number): ToolCall | undefined {\n\tif (!match.name) return undefined;\n\tif (/<\\/?function\\b/i.test(match.body)) return undefined;\n\tconst params: Record<string, string> = {};\n\tlet cursor = 0;\n\tlet sawParam = false;\n\tconst paramPattern = /<param\\s+name=([\"'])(.*?)\\1\\s*>([\\s\\S]*?)<\\/param\\s*>/g;\n\tfor (const param of match.body.matchAll(paramPattern)) {\n\t\tif (match.body.slice(cursor, param.index).trim()) return undefined;\n\t\tconst name = unescapeAttribute(param[2] ?? \"\");\n\t\tif (!name || name in params) return undefined;\n\t\tparams[name] = unescapeXmlText((param[3] ?? \"\").trim());\n\t\tcursor = param.index + param[0].length;\n\t\tsawParam = true;\n\t}\n\tif (match.body.slice(cursor).trim()) return undefined;\n\tif (!sawParam) return undefined;\n\treturn {\n\t\ttype: \"toolCall\",\n\t\tid: `text-tool-${index}`,\n\t\tname: match.name,\n\t\targuments: params,\n\t\tsource: \"text-protocol\",\n\t\terrorMessage: textToolErrorMessage(match.name, names),\n\t};\n}\n\nfunction parseEnvelope(match: EnvelopeMatch, names: readonly string[], index: number): ToolCall | undefined {\n\tif (match.kind === \"pi_call\") return parsePiCallEnvelope(match, names, index);\n\tif (match.kind === \"function_xml\") return parseFunctionXmlEnvelope(match, names, index);\n\n\tconst parsed = parseJsonObject(match.body);\n\tif (!parsed) {\n\t\tconst name = extractNameFromMalformedJson(match.body);\n\t\tif (!name) return undefined;\n\t\treturn {\n\t\t\ttype: \"toolCall\",\n\t\t\tid: `text-tool-${index}`,\n\t\t\tname,\n\t\t\targuments: match.body.trim() as unknown as Record<string, unknown>,\n\t\t\trawArguments: { text: match.body.trim() },\n\t\t\tsource: \"text-protocol\",\n\t\t\terrorMessage: textToolErrorMessage(name, names),\n\t\t};\n\t}\n\n\tconst wrappedTool = isRecord(parsed.tool) ? parsed.tool : undefined;\n\tconst nameValue = parsed.name ?? (typeof parsed.tool === \"string\" ? parsed.tool : undefined) ?? wrappedTool?.name;\n\tif (typeof nameValue !== \"string\") return undefined;\n\tconst argsValue = parsed.arguments ?? parsed.args ?? wrappedTool?.arguments ?? wrappedTool?.args ?? {};\n\tconst args = coerceArguments(argsValue);\n\treturn {\n\t\ttype: \"toolCall\",\n\t\tid: `text-tool-${index}`,\n\t\tname: nameValue,\n\t\targuments: args.arguments,\n\t\trawArguments: args.rawArguments,\n\t\tsource: \"text-protocol\",\n\t\terrorMessage: textToolErrorMessage(nameValue, names),\n\t};\n}\n\nexport function normalizeTextToolProtocolOptions(\n\toption: boolean | TextToolProtocolOptions | undefined,\n): TextToolProtocolOptions | undefined {\n\tif (!option) return undefined;\n\tif (option === true) return { variant: DEFAULT_TEXT_TOOL_PROTOCOL_VARIANT };\n\treturn { variant: option.variant ?? DEFAULT_TEXT_TOOL_PROTOCOL_VARIANT };\n}\n\nfunction functionXmlParamValue(value: unknown): string {\n\tif (typeof value === \"string\") return value;\n\tconst json = JSON.stringify(value);\n\treturn json ?? String(value);\n}\n\nfunction formatFunctionXmlEnvelope(toolName: string, argsJson: string): string {\n\tconst args = parseJsonObject(argsJson) ?? {};\n\tconst params = Object.entries(args)\n\t\t.map(\n\t\t\t([name, value]) =>\n\t\t\t\t`<param name=\"${escapeAttribute(name)}\">${escapeXmlText(functionXmlParamValue(value))}</param>`,\n\t\t)\n\t\t.join(\"\");\n\treturn `<function name=\"${escapeAttribute(toolName)}\">${params}</function>`;\n}\n\n/**\n * Renders one call as the wire text for `variant`. Exported read-only for the history round-trip\n * render: it reuses this so a prior text-protocol call is echoed back to the model in the\n * exact dialect the primer taught it, instead of a second parallel formatting path.\n */\nexport function formatVariantEnvelope(variant: TextToolProtocolVariant, toolName: string, argsJson: string): string {\n\tif (variant === \"tool-call\") return `<tool_call>{\"name\":\"${toolName}\",\"arguments\":${argsJson}}</tool_call>`;\n\tif (variant === \"fenced-json\") return `\\`\\`\\`tool_call\\n{\"name\":\"${toolName}\",\"arguments\":${argsJson}}\\n\\`\\`\\``;\n\tif (variant === \"function-xml\") return formatFunctionXmlEnvelope(toolName, argsJson);\n\treturn `<pi:call name=\"${escapeAttribute(toolName)}\">${argsJson}</pi:call>`;\n}\n\nfunction schemaRecord(value: unknown): Record<string, unknown> | undefined {\n\treturn isRecord(value) ? value : undefined;\n}\n\nfunction firstString(value: unknown): string | undefined {\n\treturn typeof value === \"string\" ? value : undefined;\n}\n\nfunction schemaType(schema: Record<string, unknown>): string | undefined {\n\tconst type = schema.type;\n\tif (typeof type === \"string\") return type;\n\tif (!Array.isArray(type)) return undefined;\n\treturn type.filter(firstString).find((entry) => entry !== \"null\");\n}\n\nfunction stringExampleForProperty(propertyName: string | undefined): string {\n\tif (propertyName === \"path\") return \"src/index.ts\";\n\tif (propertyName === \"command\") return \"echo ok\";\n\tif (propertyName === \"oldText\") return \"foo\";\n\tif (propertyName === \"newText\") return \"bar\";\n\tif (propertyName === \"content\") return \"text\";\n\treturn \"value\";\n}\n\nfunction exampleValueForSchema(schemaValue: unknown, propertyName?: string): unknown {\n\tconst schema = schemaRecord(schemaValue);\n\tif (!schema) return stringExampleForProperty(propertyName);\n\tconst constValue = schema.const;\n\tif (constValue !== undefined) return constValue;\n\tconst enumValues = Array.isArray(schema.enum) ? schema.enum : [];\n\tif (enumValues.length > 0) return enumValues[0];\n\tconst defaultValue = schema.default;\n\tif (defaultValue !== undefined) return defaultValue;\n\tconst type = schemaType(schema);\n\tif (type === \"number\" || type === \"integer\") return 1;\n\tif (type === \"boolean\") return true;\n\tif (type === \"array\") return [exampleValueForSchema(schema.items, propertyName)];\n\tif (type === \"object\") return exampleArgumentsForParameters(schema);\n\treturn stringExampleForProperty(propertyName);\n}\n\nfunction requiredPropertyNames(parameters: Record<string, unknown> | undefined): string[] {\n\treturn Array.isArray(parameters?.required) ? parameters.required.filter(firstString) : [];\n}\n\nfunction exampleArgumentsForParameters(parametersValue: unknown): Record<string, unknown> {\n\tconst parameters = schemaRecord(parametersValue);\n\tconst properties = schemaRecord(parameters?.properties);\n\tif (!properties) return {};\n\tconst args: Record<string, unknown> = {};\n\tfor (const name of requiredPropertyNames(parameters)) {\n\t\targs[name] = exampleValueForSchema(properties[name], name);\n\t}\n\treturn args;\n}\n\nfunction typeLabel(schemaValue: unknown): string {\n\tconst schema = schemaRecord(schemaValue);\n\tif (!schema) return \"string\";\n\tconst enumValues = Array.isArray(schema.enum) ? schema.enum : [];\n\tif (enumValues.length > 0 && enumValues.every((entry) => [\"string\", \"number\", \"boolean\"].includes(typeof entry))) {\n\t\treturn enumValues.map(String).join(\"|\");\n\t}\n\tconst type = schemaType(schema);\n\tif (type === \"integer\" || type === \"number\") return \"number\";\n\tif (type === \"boolean\") return \"bool\";\n\tif (type === \"array\") return `${typeLabel(schema.items)}[]`;\n\tif (type === \"object\") {\n\t\tconst properties = schemaRecord(schema.properties);\n\t\tif (!properties) return \"{}\";\n\t\tconst entries = Object.entries(properties)\n\t\t\t.slice(0, 4)\n\t\t\t.map(([name, value]) => `${name}:${typeLabel(value)}`);\n\t\tconst suffix = Object.keys(properties).length > entries.length ? \",...\" : \"\";\n\t\treturn `{${entries.join(\",\")}${suffix}}`;\n\t}\n\treturn \"string\";\n}\n\nfunction orderedPropertyNames(properties: Record<string, unknown>, required: readonly string[]): string[] {\n\tconst requiredSet = new Set(required);\n\treturn [\n\t\t...required.filter((name) => name in properties),\n\t\t...Object.keys(properties).filter((name) => !requiredSet.has(name)),\n\t];\n}\n\nfunction formatDefault(value: unknown): string {\n\tconst json = JSON.stringify(value);\n\treturn json ? `=${json}` : \"\";\n}\n\nfunction formatToolProjection(tool: Tool): string {\n\tconst parameters = schemaRecord(tool.parameters);\n\tconst properties = schemaRecord(parameters?.properties);\n\tconst required = requiredPropertyNames(parameters);\n\tconst requiredSet = new Set(required);\n\tconst args = properties\n\t\t? orderedPropertyNames(properties, required)\n\t\t\t\t.map((name) => {\n\t\t\t\t\tconst schema = schemaRecord(properties[name]);\n\t\t\t\t\tconst optional = requiredSet.has(name) ? \"\" : \"?\";\n\t\t\t\t\tconst defaultText = schema && \"default\" in schema ? formatDefault(schema.default) : \"\";\n\t\t\t\t\treturn `${name}:${typeLabel(schema)}${optional}${defaultText}`;\n\t\t\t\t})\n\t\t\t\t.join(\", \")\n\t\t: \"\";\n\tconst description = tool.description.replace(/\\s+/g, \" \").trim();\n\treturn `${tool.name}(${args}) - ${description}`;\n}\n\nfunction toolHasArrayParameter(tool: Tool): boolean {\n\tconst parameters = schemaRecord(tool.parameters);\n\tconst properties = schemaRecord(parameters?.properties);\n\tif (!properties) return false;\n\treturn Object.values(properties).some((schemaValue) => schemaType(schemaRecord(schemaValue) ?? {}) === \"array\");\n}\n\nfunction exampleTools(tools: readonly Tool[]): Tool[] {\n\tconst examples: Tool[] = [];\n\tconst readTool = tools.find((tool) => tool.name === \"read\");\n\tif (readTool) examples.push(readTool);\n\tif (examples.length === 0) examples.push(tools[0]);\n\tconst editTool = tools.find((tool) => tool.name === \"edit\" && !examples.includes(tool));\n\tconst arrayTool = editTool ?? tools.find((tool) => !examples.includes(tool) && toolHasArrayParameter(tool));\n\tif (arrayTool) examples.push(arrayTool);\n\treturn examples;\n}\n\nfunction protocolHeader(variant: TextToolProtocolVariant): string[] {\n\treturn [\n\t\t\"Text tool-call protocol is enabled. To call a tool, emit an envelope in exactly this shape:\",\n\t\tformatVariantEnvelope(variant, \"TOOL\", '{\"arg\":\"value\"}'),\n\t\t\"Arguments are one JSON object: double-quoted keys/strings, arrays as [ ] (never a quoted string), optional args omitted rather than null.\",\n\t\t\"Reasoning may appear as prose before an envelope, never inside one.\",\n\t\t\"Emit several envelopes in one reply to call multiple tools in parallel; each is executed and answered on its own.\",\n\t\t\"Files, directories, searches, edits, writes, and shell commands always go through a tool envelope - never type out shell commands, file paths, or invented results yourself.\",\n\t];\n}\n\nexport function generateTextToolProtocolPrimer(tools: readonly Tool[], options?: TextToolProtocolOptions): string {\n\tif (tools.length === 0) return \"\";\n\tconst variant = options?.variant ?? DEFAULT_TEXT_TOOL_PROTOCOL_VARIANT;\n\tconst lines = [...protocolHeader(variant), \"Examples:\"];\n\tfor (const tool of exampleTools(tools)) {\n\t\tlines.push(\n\t\t\tformatVariantEnvelope(variant, tool.name, JSON.stringify(exampleArgumentsForParameters(tool.parameters))),\n\t\t);\n\t}\n\tlines.push(\"Available tools:\");\n\tfor (const tool of tools) {\n\t\tlines.push(formatToolProjection(tool));\n\t}\n\treturn lines.join(\"\\n\");\n}\n\nexport function parseTextToolCalls(text: string, knownTools: readonly Tool[]): ParsedTextToolCalls {\n\tif (knownTools.length === 0) return { calls: [], text, attempted: false };\n\tconst matches = findToolEnvelopes(text);\n\tif (matches.length === 0) return { calls: [], text, attempted: false };\n\tconst names = [...knownToolNames(knownTools)];\n\tconst parsed: ParsedMatch[] = [];\n\tfor (const [index, match] of matches.entries()) {\n\t\tconst call = parseEnvelope(match, names, index + 1);\n\t\tif (call) parsed.push({ match, call });\n\t}\n\tconst selected = selectNonOverlappingParsedMatches(parsed);\n\tif (selected.length === 0) {\n\t\treturn { calls: [], text, attempted: true, failure: hasOverlap(matches) ? \"overlap\" : \"unrecognized\" };\n\t}\n\tconst remainder = remainingText(\n\t\ttext,\n\t\tselected.map((entry) => entry.match),\n\t);\n\treturn { calls: selected.map((entry) => entry.call), text: remainder.trim(), attempted: true };\n}\n"]}
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/** Ordered dialects used by protocol calibration, from most compact/native-like to broadest fallback. */
|
|
2
|
+
export const TEXT_TOOL_PROTOCOL_VARIANTS = [
|
|
3
|
+
"tool-tag",
|
|
4
|
+
"tool-call",
|
|
5
|
+
"fenced-json",
|
|
6
|
+
"function-xml",
|
|
7
|
+
];
|
|
8
|
+
export function isTextToolProtocolVariant(value) {
|
|
9
|
+
return typeof value === "string" && TEXT_TOOL_PROTOCOL_VARIANTS.includes(value);
|
|
10
|
+
}
|
|
1
11
|
const DEFAULT_TEXT_TOOL_PROTOCOL_VARIANT = "tool-tag";
|
|
2
12
|
function isRecord(value) {
|
|
3
13
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|