@yolk-sdk/agent 0.1.0-canary.68 → 0.1.0-canary.69
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/README.md +3 -1
- package/dist/providers/openai/provider.d.mts +8 -2
- package/dist/providers/openai/provider.d.mts.map +1 -1
- package/dist/providers/openai/provider.mjs +4 -1
- package/dist/providers/openai/provider.mjs.map +1 -1
- package/dist/providers/vercel/ai-gateway-provider.d.mts.map +1 -1
- package/dist/providers/vercel/ai-gateway-provider.mjs +1 -0
- package/dist/providers/vercel/ai-gateway-provider.mjs.map +1 -1
- package/dist/skillset/merge.d.mts +2 -2
- package/dist/skillset/name.d.mts +2 -2
- package/dist/skillset/name.d.mts.map +1 -1
- package/package.json +1 -1
- package/src/providers/openai/provider.ts +17 -2
- package/src/providers/vercel/ai-gateway-provider.ts +1 -0
package/README.md
CHANGED
|
@@ -172,7 +172,9 @@ Gateway API key or Vercel OIDC token as `apiKey`; `maxCompletionTokens` is sent
|
|
|
172
172
|
`VERCEL_OIDC_TOKEN`, and requires integer
|
|
173
173
|
`AI_GATEWAY_MAX_COMPLETION_TOKENS`. Model ids are opaque `provider/model` strings. Hosts may set
|
|
174
174
|
`fallbackModels`, provider `routing`, and optional `http-referer` / `x-title` attribution headers.
|
|
175
|
-
|
|
175
|
+
A request `reasoningEffort` is sent as Gateway `{ reasoning: { effort } }` for any opaque model id;
|
|
176
|
+
hosts remain responsible for offering only efforts supported by the selected model. Required
|
|
177
|
+
authorization and JSON headers cannot be replaced through `extraHeaders`. Only override
|
|
176
178
|
`chatCompletionsUrl` with a trusted proxy because it receives the bearer credential.
|
|
177
179
|
|
|
178
180
|
Grok subscription access uses `https://cli-chat-proxy.grok.com/v1/responses`, not the xAI developer
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Effect, Layer, Redacted } from "effect";
|
|
2
|
+
import { AgentReasoningEffort } from "@yolk-sdk/agent/protocol";
|
|
2
3
|
import { HttpClient } from "effect/unstable/http";
|
|
3
4
|
import { LLMError, LLMProvider, LLMRequest } from "@yolk-sdk/agent/loop";
|
|
4
5
|
|
|
@@ -11,8 +12,9 @@ type OpenAiProviderConfig = {
|
|
|
11
12
|
readonly chatCompletionsUrl?: string;
|
|
12
13
|
readonly maxCompletionTokens: number; /** Selects the compatible endpoint's output-limit parameter. Defaults to `max_completion_tokens`. */
|
|
13
14
|
readonly completionTokenField?: 'max_completion_tokens' | 'max_tokens';
|
|
14
|
-
readonly extraHeaders?: Readonly<Record<string, string>>; /** Adds
|
|
15
|
-
readonly extraBody?: Readonly<Record<string, unknown>>; /**
|
|
15
|
+
readonly extraHeaders?: Readonly<Record<string, string>>; /** Adds endpoint extensions; enabled reasoning and canonical model/messages/limit/stream fields win. */
|
|
16
|
+
readonly extraBody?: Readonly<Record<string, unknown>>; /** Opts into a compatible endpoint's `{ reasoning: { effort } }` request extension. */
|
|
17
|
+
readonly reasoningEffortFormat?: 'reasoning-object'; /** Customizes safe error metadata for a branded OpenAI-compatible endpoint. */
|
|
16
18
|
readonly providerIdentity?: OpenAiProviderIdentity;
|
|
17
19
|
readonly apiKey: Redacted.Redacted<string>;
|
|
18
20
|
};
|
|
@@ -64,6 +66,9 @@ type OpenAiRequestBody = {
|
|
|
64
66
|
readonly max_completion_tokens?: number;
|
|
65
67
|
readonly max_tokens?: number;
|
|
66
68
|
readonly stream: false;
|
|
69
|
+
readonly reasoning?: {
|
|
70
|
+
readonly effort: AgentReasoningEffort;
|
|
71
|
+
};
|
|
67
72
|
readonly tools?: ReadonlyArray<OpenAiTool>;
|
|
68
73
|
readonly parallel_tool_calls?: true;
|
|
69
74
|
};
|
|
@@ -71,6 +76,7 @@ type OpenAiRequestBodyConfig = {
|
|
|
71
76
|
readonly maxCompletionTokens: number;
|
|
72
77
|
readonly completionTokenField?: 'max_completion_tokens' | 'max_tokens';
|
|
73
78
|
readonly extraBody?: Readonly<Record<string, unknown>>;
|
|
79
|
+
readonly reasoningEffortFormat?: 'reasoning-object';
|
|
74
80
|
readonly providerName?: string;
|
|
75
81
|
};
|
|
76
82
|
declare const toOpenAiRequestBody: (request: LLMRequest, config: OpenAiRequestBodyConfig) => Effect.Effect<OpenAiRequestBody, LLMError>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.d.mts","names":[],"sources":["../../../src/providers/openai/provider.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"provider.d.mts","names":[],"sources":["../../../src/providers/openai/provider.ts"],"mappings":";;;;;;KA8CK,sBAAA;EAAA,SACM,EAAA;EAAA,SACA,IAAI;AAAA;AAAA,KAGH,oBAAA;EAAA,SACD,kBAAA;EAAA,SACA,mBAAA,UAFC;EAAA,SAID,oBAAA;EAAA,SACA,YAAA,GAAe,QAAA,CAAS,MAAA,mBAAA;EAAA,SAExB,SAAA,GAAY,QAAA,CAAS,MAAA,oBAAA;EAAA,SAErB,qBAAA,uBAEmB;EAAA,SAAnB,gBAAA,GAAmB,sBAAA;EAAA,SACnB,MAAA,EAAQ,QAAA,CAAS,QAAA;AAAA;AAAA,KAGvB,qBAAA;EAAA,SACM,IAAA;EAAA,SACA,IAAI;AAAA;AAAA,KAGV,sBAAA;EAAA,SACM,IAAA;EAAA,SACA,SAAA;IAAA,SACE,GAAA;EAAA;AAAA;AAAA,KAIR,iBAAA,YAA6B,aAAA,CAAc,qBAAA,GAAwB,sBAAA;AAAA,KAEnE,cAAA;EAAA,SACM,EAAA;EAAA,SACA,IAAA;EAAA,SACA,QAAA;IAAA,SACE,IAAA;IAAA,SACA,SAAA;EAAA;AAAA;AAAA,KAIR,aAAA;EAAA,SACU,IAAA;EAAA,SAAyB,OAAA;AAAA;EAAA,SACzB,IAAA;EAAA,SAAuB,OAAA,EAAS,iBAAA;AAAA;EAAA,SAEhC,IAAA;EAAA,SACA,OAAA;EAAA,SACA,UAAA,GAAa,aAAA,CAAc,cAAA;AAAA;EAAA,SAE3B,IAAA;EAAA,SAAuB,YAAA;EAAA,SAA+B,OAAA;AAAA;AAAA,KAEhE,UAAA;EAAA,SACM,IAAA;EAAA,SACA,QAAA;IAAA,SACE,IAAA;IAAA,SACA,WAAA;IAAA,SACA,UAAA;EAAA;AAAA;AAAA,KAIR,iBAAA;EAAA,SACM,KAAA;EAAA,SACA,QAAA,EAAU,aAAA,CAAc,aAAA;EAAA,SACxB,qBAAA;EAAA,SACA,UAAA;EAAA,SACA,MAAA;EAAA,SACA,SAAA;IAAA,SACE,MAAA,EAAQ,oBAAA;EAAA;EAAA,SAEV,KAAA,GAAQ,aAAA,CAAc,UAAA;EAAA,SACtB,mBAAA;AAAA;AAAA,KAGN,uBAAA;EAAA,SACM,mBAAA;EAAA,SACA,oBAAA;EAAA,SACA,SAAA,GAAY,QAAQ,CAAC,MAAA;EAAA,SACrB,qBAAA;EAAA,SACA,YAAA;AAAA;AAAA,cA8QE,mBAAA,GACX,OAAA,EAAS,UAAA,EACT,MAAA,EAAQ,uBAAA,KACP,MAAA,CAAO,MAAA,CAAO,iBAAA,EAAmB,QAAA;AAAA,cAgPvB,uBAAA,GAA2B,MAAA,EAAQ,oBAAA,KAAoB,KAAA,CAAA,KAAA,CAAA,WAAA,SAAA,UAAA,CAAA,UAAA;AAAA,cAevD,mBAAA,EAAmB,KAAA,CAAA,KAAA,CAAA,WAAA,EAAA,QAAA"}
|
|
@@ -161,8 +161,10 @@ const toOpenAiRequestBody = (request, config) => Effect.gen(function* () {
|
|
|
161
161
|
content: request.systemPrompt
|
|
162
162
|
}, ...yield* Effect.forEach(request.messages, (message) => toOpenAiMessage(message, providerName))];
|
|
163
163
|
const completionTokenLimit = config.completionTokenField === "max_tokens" ? { max_tokens: config.maxCompletionTokens } : { max_completion_tokens: config.maxCompletionTokens };
|
|
164
|
+
const reasoning = config.reasoningEffortFormat === "reasoning-object" && request.reasoningEffort !== void 0 ? { reasoning: { effort: request.reasoningEffort } } : {};
|
|
164
165
|
const body = {
|
|
165
166
|
...config.extraBody,
|
|
167
|
+
...reasoning,
|
|
166
168
|
model: request.model,
|
|
167
169
|
messages,
|
|
168
170
|
...completionTokenLimit,
|
|
@@ -233,7 +235,8 @@ const sendOpenAiRequest = (config, request, client) => Effect.gen(function* () {
|
|
|
233
235
|
maxCompletionTokens: config.maxCompletionTokens,
|
|
234
236
|
providerName: providerIdentity.name,
|
|
235
237
|
...config.completionTokenField === void 0 ? {} : { completionTokenField: config.completionTokenField },
|
|
236
|
-
...config.extraBody === void 0 ? {} : { extraBody: config.extraBody }
|
|
238
|
+
...config.extraBody === void 0 ? {} : { extraBody: config.extraBody },
|
|
239
|
+
...config.reasoningEffortFormat === void 0 ? {} : { reasoningEffortFormat: config.reasoningEffortFormat }
|
|
237
240
|
})), `Could not serialize ${providerIdentity.name} request`);
|
|
238
241
|
const httpRequest = HttpClientRequest.post(config.chatCompletionsUrl ?? "https://api.openai.com/v1/chat/completions").pipe(HttpClientRequest.setHeaders({
|
|
239
242
|
...config.extraHeaders,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.mjs","names":["LLMTextDelta"],"sources":["../../../src/providers/openai/provider.ts"],"sourcesContent":["import { Config, Context, Effect, Layer, Option, Redacted, Stream } from 'effect'\nimport {\n FetchHttpClient,\n HttpClient,\n HttpClientRequest,\n type HttpClientError,\n type HttpClientResponse\n} from 'effect/unstable/http'\nimport * as Schema from 'effect/Schema'\nimport {\n ToolCall,\n AgentInputUsage,\n AgentOutputUsage,\n AgentUsage,\n attachmentSourceText,\n attachmentSourceUrl,\n assistantContent,\n assistantHostToolCalls,\n isTextDocumentMimeType,\n messageContextText,\n replaceLoneSurrogatesDeep,\n prependMessageContextToContent,\n type AgentMessage,\n type Content,\n type ContentPart,\n type ToolDef\n} from '@yolk-sdk/agent/protocol'\nimport {\n LLMError,\n LLMDone,\n LLMProvider,\n LLMTextDelta,\n LLMToolCall,\n LLMUsage,\n type LLMEvent,\n type LLMRequest\n} from '@yolk-sdk/agent/loop'\nimport {\n classifyProviderFailure,\n providerErrorInfo,\n providerFailureCause,\n providerFailureRetryable\n} from '../provider-error.ts'\nimport { validateProviderTranscript } from '../transcript.ts'\n\ntype OpenAiProviderIdentity = {\n readonly id: string\n readonly name: string\n}\n\nexport type OpenAiProviderConfig = {\n readonly chatCompletionsUrl?: string\n readonly maxCompletionTokens: number\n /** Selects the compatible endpoint's output-limit parameter. Defaults to `max_completion_tokens`. */\n readonly completionTokenField?: 'max_completion_tokens' | 'max_tokens'\n readonly extraHeaders?: Readonly<Record<string, string>>\n /** Adds compatible endpoint extensions; canonical model/messages/limit/stream fields win. */\n readonly extraBody?: Readonly<Record<string, unknown>>\n /** Customizes safe error metadata for a branded OpenAI-compatible endpoint. */\n readonly providerIdentity?: OpenAiProviderIdentity\n readonly apiKey: Redacted.Redacted<string>\n}\n\ntype OpenAiTextContentPart = {\n readonly type: 'text'\n readonly text: string\n}\n\ntype OpenAiImageContentPart = {\n readonly type: 'image_url'\n readonly image_url: {\n readonly url: string\n }\n}\n\ntype OpenAiUserContent = string | ReadonlyArray<OpenAiTextContentPart | OpenAiImageContentPart>\n\ntype OpenAiToolCall = {\n readonly id: string\n readonly type: 'function'\n readonly function: {\n readonly name: string\n readonly arguments: string\n }\n}\n\ntype OpenAiMessage =\n | { readonly role: 'system'; readonly content: string }\n | { readonly role: 'user'; readonly content: OpenAiUserContent }\n | {\n readonly role: 'assistant'\n readonly content: string | null\n readonly tool_calls?: ReadonlyArray<OpenAiToolCall>\n }\n | { readonly role: 'tool'; readonly tool_call_id: string; readonly content: string }\n\ntype OpenAiTool = {\n readonly type: 'function'\n readonly function: {\n readonly name: string\n readonly description: string\n readonly parameters: unknown\n }\n}\n\ntype OpenAiRequestBody = {\n readonly model: string\n readonly messages: ReadonlyArray<OpenAiMessage>\n readonly max_completion_tokens?: number\n readonly max_tokens?: number\n readonly stream: false\n readonly tools?: ReadonlyArray<OpenAiTool>\n readonly parallel_tool_calls?: true\n}\n\ntype OpenAiRequestBodyConfig = {\n readonly maxCompletionTokens: number\n readonly completionTokenField?: 'max_completion_tokens' | 'max_tokens'\n readonly extraBody?: Readonly<Record<string, unknown>>\n readonly providerName?: string\n}\n\nconst defaultOpenAiProviderIdentity: OpenAiProviderIdentity = {\n id: 'openai',\n name: 'OpenAI'\n}\n\nclass OpenAiFunctionResponse extends Schema.Class<OpenAiFunctionResponse>('OpenAiFunctionResponse')(\n {\n name: Schema.String,\n arguments: Schema.String\n }\n) {}\n\nclass OpenAiToolCallResponse extends Schema.Class<OpenAiToolCallResponse>('OpenAiToolCallResponse')(\n {\n id: Schema.String,\n type: Schema.Literals(['function']),\n function: OpenAiFunctionResponse\n }\n) {}\n\nclass OpenAiMessageResponse extends Schema.Class<OpenAiMessageResponse>('OpenAiMessageResponse')({\n content: Schema.NullOr(Schema.String),\n tool_calls: Schema.optional(Schema.Array(OpenAiToolCallResponse))\n}) {}\n\nclass OpenAiChoiceResponse extends Schema.Class<OpenAiChoiceResponse>('OpenAiChoiceResponse')({\n message: OpenAiMessageResponse,\n finish_reason: Schema.optional(Schema.NullOr(Schema.String))\n}) {}\n\nclass OpenAiPromptTokensDetails extends Schema.Class<OpenAiPromptTokensDetails>(\n 'OpenAiPromptTokensDetails'\n)({\n cached_tokens: Schema.optional(Schema.Number)\n}) {}\n\nclass OpenAiCompletionTokensDetails extends Schema.Class<OpenAiCompletionTokensDetails>(\n 'OpenAiCompletionTokensDetails'\n)({\n reasoning_tokens: Schema.optional(Schema.Number)\n}) {}\n\nclass OpenAiUsageResponse extends Schema.Class<OpenAiUsageResponse>('OpenAiUsageResponse')({\n prompt_tokens: Schema.Number,\n completion_tokens: Schema.Number,\n prompt_tokens_details: Schema.optional(OpenAiPromptTokensDetails),\n completion_tokens_details: Schema.optional(OpenAiCompletionTokensDetails)\n}) {}\n\nclass OpenAiChatCompletionResponse extends Schema.Class<OpenAiChatCompletionResponse>(\n 'OpenAiChatCompletionResponse'\n)({\n choices: Schema.Array(OpenAiChoiceResponse),\n usage: Schema.optional(OpenAiUsageResponse)\n}) {}\n\nclass OpenAiConfig extends Context.Service<OpenAiConfig, OpenAiProviderConfig>()(\n '@app/OpenAiConfig'\n) {}\n\nconst OpenAiConfigLayer = Layer.effect(\n OpenAiConfig,\n Effect.gen(function* () {\n const apiKey = yield* Config.redacted('OPENAI_API_KEY')\n const maxCompletionTokens = yield* Config.int('OPENAI_MAX_COMPLETION_TOKENS')\n return { apiKey, maxCompletionTokens }\n }).pipe(\n Effect.mapError(\n () =>\n new LLMError({\n cause: 'provider_error',\n message: 'OpenAI provider environment configuration missing',\n retryable: false\n })\n )\n )\n)\n\nconst unknownToMessage = (error: unknown) =>\n error instanceof Error ? error.message : String(error)\n\nconst encodeJsonString = (value: unknown, message: string) =>\n Schema.encodeUnknownEffect(Schema.UnknownFromJsonString)(value).pipe(\n Effect.mapError(\n error =>\n new LLMError({\n cause: 'provider_error',\n message: `${message}: ${unknownToMessage(error)}`,\n retryable: false\n })\n )\n )\n\nconst decodeJsonString = (raw: string, message: string) =>\n Schema.decodeUnknownEffect(Schema.UnknownFromJsonString)(raw).pipe(\n Effect.mapError(\n error =>\n new LLMError({\n cause: 'invalid_response',\n message: `${message}: ${unknownToMessage(error)}`,\n retryable: false\n })\n )\n )\n\nconst unsupportedContentError = (contentType: string, providerName: string) =>\n new LLMError({\n cause: 'provider_error',\n message: `${contentType} content is not supported by the ${providerName} provider yet`,\n retryable: false\n })\n\nconst textDocumentToOpenAiPart = (\n part: Extract<ContentPart, { readonly _tag: 'Document' }>,\n providerName: string\n) =>\n attachmentSourceText(part.source).pipe(\n Effect.mapError(() => unsupportedContentError('Invalid document text', providerName)),\n Effect.flatMap(\n Option.match({\n onNone: () =>\n Effect.fail(unsupportedContentError('Unresolved document source', providerName)),\n onSome: text => {\n const block: OpenAiTextContentPart = {\n type: 'text',\n text: `Document: ${part.title ?? part.filename}\\n\\n${text}`\n }\n\n return Effect.succeed(block)\n }\n })\n )\n )\n\nconst contentPartToUserPart = (\n part: ContentPart,\n providerName: string\n): Effect.Effect<OpenAiTextContentPart | OpenAiImageContentPart, LLMError> => {\n switch (part._tag) {\n case 'Text':\n return Effect.succeed({ type: 'text', text: part.text })\n case 'Image':\n return Option.match(attachmentSourceUrl(part.source, part.mimeType), {\n onNone: () => Effect.fail(unsupportedContentError('Unresolved image source', providerName)),\n onSome: url => Effect.succeed({ type: 'image_url', image_url: { url } })\n })\n case 'Document':\n return isTextDocumentMimeType(part.mimeType)\n ? textDocumentToOpenAiPart(part, providerName)\n : Effect.fail(unsupportedContentError('Document', providerName))\n case 'Audio':\n return Effect.fail(unsupportedContentError('Audio', providerName))\n }\n}\n\nconst contentToUserContent = (\n content: Content,\n providerName: string\n): Effect.Effect<OpenAiUserContent, LLMError> =>\n typeof content === 'string'\n ? Effect.succeed(content)\n : Effect.forEach(content, part => contentPartToUserPart(part, providerName))\n\nconst contentPartToText = (\n part: ContentPart,\n owner: string,\n providerName: string\n): Effect.Effect<string, LLMError> => {\n switch (part._tag) {\n case 'Text':\n return Effect.succeed(part.text)\n case 'Image':\n return Effect.fail(unsupportedContentError(`${owner} image`, providerName))\n case 'Document':\n return Effect.fail(unsupportedContentError(`${owner} document`, providerName))\n case 'Audio':\n return Effect.fail(unsupportedContentError(`${owner} audio`, providerName))\n }\n}\n\nconst contentToText = (\n content: Content,\n owner: string,\n providerName: string\n): Effect.Effect<string, LLMError> =>\n typeof content === 'string'\n ? Effect.succeed(content)\n : Effect.forEach(content, part => contentPartToText(part, owner, providerName)).pipe(\n Effect.map(textParts => textParts.join('\\n'))\n )\n\nconst serializeToolArguments = (params: unknown, providerName: string) =>\n encodeJsonString(params, `Could not serialize ${providerName} tool arguments`)\n\nconst toolCallToOpenAiToolCall = (\n call: ToolCall,\n providerName: string\n): Effect.Effect<OpenAiToolCall, LLMError> =>\n Effect.gen(function* () {\n return {\n id: call.id,\n type: 'function',\n function: {\n name: call.name,\n arguments: yield* serializeToolArguments(call.params, providerName)\n }\n }\n })\n\nconst toOpenAiMessage = (\n message: AgentMessage,\n providerName: string\n): Effect.Effect<OpenAiMessage, LLMError> =>\n Effect.gen(function* () {\n switch (message._tag) {\n case 'User':\n return {\n role: 'user',\n content: yield* contentToUserContent(\n prependMessageContextToContent(message.content, messageContextText(message)),\n providerName\n )\n }\n case 'Assistant': {\n const content = prependMessageContextToContent(\n assistantContent(message),\n messageContextText(message)\n )\n const toolCalls = yield* Effect.forEach(assistantHostToolCalls(message), call =>\n toolCallToOpenAiToolCall(call, providerName)\n )\n\n if (toolCalls.length > 0) {\n return {\n role: 'assistant',\n content: yield* contentToText(content, 'Assistant', providerName),\n tool_calls: toolCalls\n }\n }\n\n return {\n role: 'assistant',\n content: yield* contentToText(content, 'Assistant', providerName)\n }\n }\n case 'ToolResult':\n return {\n role: 'tool',\n tool_call_id: message.toolCallId,\n content: yield* contentToText(\n prependMessageContextToContent(message.content, messageContextText(message)),\n 'Tool result',\n providerName\n )\n }\n }\n })\n\nconst toOpenAiTool = (tool: ToolDef): OpenAiTool => ({\n type: 'function',\n function: {\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters\n }\n})\n\nexport const toOpenAiRequestBody = (\n request: LLMRequest,\n config: OpenAiRequestBodyConfig\n): Effect.Effect<OpenAiRequestBody, LLMError> =>\n Effect.gen(function* () {\n const providerName = config.providerName ?? defaultOpenAiProviderIdentity.name\n\n if (!Number.isSafeInteger(config.maxCompletionTokens) || config.maxCompletionTokens <= 0) {\n return yield* Effect.fail(\n new LLMError({\n cause: 'validation_error',\n message: `${providerName} maxCompletionTokens must be a positive safe integer`,\n retryable: false\n })\n )\n }\n\n yield* validateProviderTranscript(request.messages)\n const systemMessage: OpenAiMessage = { role: 'system', content: request.systemPrompt }\n const requestMessages = yield* Effect.forEach(request.messages, message =>\n toOpenAiMessage(message, providerName)\n )\n const messages = [systemMessage, ...requestMessages]\n\n const completionTokenLimit =\n config.completionTokenField === 'max_tokens'\n ? { max_tokens: config.maxCompletionTokens }\n : { max_completion_tokens: config.maxCompletionTokens }\n const body: OpenAiRequestBody = {\n ...config.extraBody,\n model: request.model,\n messages,\n ...completionTokenLimit,\n stream: false\n }\n\n if (request.tools.length === 0) {\n return body\n }\n\n return {\n ...body,\n tools: request.tools.map(toOpenAiTool),\n parallel_tool_calls: true\n }\n })\n\nconst parseToolArguments = (raw: string, providerName: string) =>\n decodeJsonString(raw, `Invalid ${providerName} tool arguments JSON`)\n\nconst toLlmEvents = (\n choice: OpenAiChoiceResponse,\n providerIdentity: OpenAiProviderIdentity\n): Effect.Effect<ReadonlyArray<LLMEvent>, LLMError> =>\n Effect.gen(function* () {\n if (choice.finish_reason === 'length' || choice.finish_reason === 'content_filter') {\n return yield* Effect.fail(\n new LLMError({\n cause: 'invalid_response',\n message: `${providerIdentity.name} response stopped with ${choice.finish_reason}`,\n retryable: false,\n provider: providerErrorInfo({\n provider: providerIdentity.id,\n kind: 'invalid_response',\n providerCode: choice.finish_reason\n })\n })\n )\n }\n\n const content = choice.message.content ?? ''\n const textEvents = content.length > 0 ? [LLMTextDelta.make({ text: content })] : []\n const toolCallEvents = yield* Effect.forEach(choice.message.tool_calls ?? [], call =>\n parseToolArguments(call.function.arguments, providerIdentity.name).pipe(\n Effect.map(params =>\n LLMToolCall.make({\n call: ToolCall.make({\n id: call.id,\n name: call.function.name,\n params\n })\n })\n )\n )\n )\n\n if (toolCallEvents.length > 0) {\n return [...textEvents, ...toolCallEvents, LLMDone.make({ stopReason: 'tool_use' })]\n }\n\n return [...textEvents, LLMDone.make({ stopReason: 'stop' })]\n })\n\nconst toAgentUsage = (usage: OpenAiUsageResponse) =>\n AgentUsage.make({\n input: AgentInputUsage.make({\n total: usage.prompt_tokens,\n uncached: usage.prompt_tokens - (usage.prompt_tokens_details?.cached_tokens ?? 0),\n cacheRead: usage.prompt_tokens_details?.cached_tokens\n }),\n output: AgentOutputUsage.make({\n total: usage.completion_tokens,\n reasoning: usage.completion_tokens_details?.reasoning_tokens,\n text: usage.completion_tokens - (usage.completion_tokens_details?.reasoning_tokens ?? 0)\n })\n })\n\nconst toHttpClientLlmError =\n (providerIdentity: OpenAiProviderIdentity, retryable: boolean) =>\n (error: HttpClientError.HttpClientError) =>\n new LLMError({\n cause: 'provider_error',\n message: `${providerIdentity.name} request failed: ${error.message}`,\n retryable,\n provider: providerErrorInfo({\n provider: providerIdentity.id,\n kind: retryable ? 'network' : 'unknown'\n })\n })\n\nconst parseOpenAiResponseJson = (\n response: HttpClientResponse.HttpClientResponse,\n providerName: string\n): Effect.Effect<unknown, LLMError> =>\n response.json.pipe(\n Effect.mapError(\n error =>\n new LLMError({\n cause: 'invalid_response',\n message: `Could not parse ${providerName} response JSON: ${error.message}`,\n retryable: false\n })\n )\n )\n\nconst sendOpenAiRequest = (\n config: OpenAiProviderConfig,\n request: LLMRequest,\n client: HttpClient.HttpClient\n): Effect.Effect<ReadonlyArray<LLMEvent>, LLMError> =>\n Effect.gen(function* () {\n const providerIdentity = config.providerIdentity ?? defaultOpenAiProviderIdentity\n const body = yield* toOpenAiRequestBody(request, {\n maxCompletionTokens: config.maxCompletionTokens,\n providerName: providerIdentity.name,\n ...(config.completionTokenField === undefined\n ? {}\n : { completionTokenField: config.completionTokenField }),\n ...(config.extraBody === undefined ? {} : { extraBody: config.extraBody })\n })\n // Replayed transcripts can carry lone surrogates; harden the lowered\n // body so one bad historical string cannot poison every model call.\n const serializedBody = yield* encodeJsonString(\n replaceLoneSurrogatesDeep(body),\n `Could not serialize ${providerIdentity.name} request`\n )\n\n const httpRequest = HttpClientRequest.post(\n config.chatCompletionsUrl ?? 'https://api.openai.com/v1/chat/completions'\n ).pipe(\n HttpClientRequest.setHeaders({\n ...config.extraHeaders,\n accept: 'application/json',\n authorization: `Bearer ${Redacted.value(config.apiKey)}`,\n 'content-type': 'application/json'\n }),\n HttpClientRequest.bodyText(serializedBody, 'application/json')\n )\n const response = yield* client\n .execute(httpRequest)\n .pipe(Effect.mapError(toHttpClientLlmError(providerIdentity, true)))\n\n if (response.status < 200 || response.status >= 300) {\n const errorText = yield* response.text.pipe(\n Effect.mapError(\n error =>\n new LLMError({\n cause: 'provider_error',\n message: `Could not read ${providerIdentity.name} error body: ${error.message}`,\n retryable: false\n })\n )\n )\n\n const provider = classifyProviderFailure({\n provider: providerIdentity.id,\n status: response.status,\n headers: response.headers,\n body: errorText\n })\n\n return yield* Effect.fail(\n new LLMError({\n cause: providerFailureCause(provider.kind),\n message: `${providerIdentity.name} returned ${response.status}`,\n retryable: providerFailureRetryable(provider.kind),\n provider\n })\n )\n }\n\n const json = yield* parseOpenAiResponseJson(response, providerIdentity.name)\n const parsed = yield* Schema.decodeUnknownEffect(OpenAiChatCompletionResponse)(json).pipe(\n Effect.mapError(\n error =>\n new LLMError({\n cause: 'invalid_response',\n message: `Invalid ${providerIdentity.name} response: ${unknownToMessage(error)}`,\n retryable: false\n })\n )\n )\n const choice = parsed.choices[0]\n\n if (choice === undefined) {\n return yield* Effect.fail(\n new LLMError({\n cause: 'invalid_response',\n message: `${providerIdentity.name} response contained no choices`,\n retryable: false\n })\n )\n }\n\n const events = yield* toLlmEvents(choice, providerIdentity)\n\n if (parsed.usage === undefined) {\n return events\n }\n\n return [...events, LLMUsage.make({ usage: toAgentUsage(parsed.usage) })]\n }).pipe(\n Effect.withSpan(`${config.providerIdentity?.id ?? defaultOpenAiProviderIdentity.id}.stream`)\n )\n\nexport const makeOpenAiProviderLayer = (config: OpenAiProviderConfig) =>\n Layer.effect(\n LLMProvider,\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n\n return LLMProvider.of({\n stream: request =>\n Stream.fromEffect(sendOpenAiRequest(config, request, client)).pipe(\n Stream.flatMap(events => Stream.fromIterable(events))\n )\n })\n })\n )\n\nexport const OpenAiProviderLayer = Layer.effect(\n LLMProvider,\n Effect.gen(function* () {\n const config = yield* OpenAiConfig\n const client = yield* HttpClient.HttpClient\n\n return LLMProvider.of({\n stream: request =>\n Stream.fromEffect(sendOpenAiRequest(config, request, client)).pipe(\n Stream.flatMap(events => Stream.fromIterable(events))\n )\n })\n })\n).pipe(Layer.provide(Layer.mergeAll(OpenAiConfigLayer, FetchHttpClient.layer)))\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"provider.mjs","names":["LLMTextDelta"],"sources":["../../../src/providers/openai/provider.ts"],"sourcesContent":["import { Config, Context, Effect, Layer, Option, Redacted, Stream } from 'effect'\nimport {\n FetchHttpClient,\n HttpClient,\n HttpClientRequest,\n type HttpClientError,\n type HttpClientResponse\n} from 'effect/unstable/http'\nimport * as Schema from 'effect/Schema'\nimport {\n ToolCall,\n AgentInputUsage,\n AgentOutputUsage,\n AgentUsage,\n attachmentSourceText,\n attachmentSourceUrl,\n assistantContent,\n assistantHostToolCalls,\n isTextDocumentMimeType,\n messageContextText,\n replaceLoneSurrogatesDeep,\n prependMessageContextToContent,\n type AgentMessage,\n type AgentReasoningEffort,\n type Content,\n type ContentPart,\n type ToolDef\n} from '@yolk-sdk/agent/protocol'\nimport {\n LLMError,\n LLMDone,\n LLMProvider,\n LLMTextDelta,\n LLMToolCall,\n LLMUsage,\n type LLMEvent,\n type LLMRequest\n} from '@yolk-sdk/agent/loop'\nimport {\n classifyProviderFailure,\n providerErrorInfo,\n providerFailureCause,\n providerFailureRetryable\n} from '../provider-error.ts'\nimport { validateProviderTranscript } from '../transcript.ts'\n\ntype OpenAiProviderIdentity = {\n readonly id: string\n readonly name: string\n}\n\nexport type OpenAiProviderConfig = {\n readonly chatCompletionsUrl?: string\n readonly maxCompletionTokens: number\n /** Selects the compatible endpoint's output-limit parameter. Defaults to `max_completion_tokens`. */\n readonly completionTokenField?: 'max_completion_tokens' | 'max_tokens'\n readonly extraHeaders?: Readonly<Record<string, string>>\n /** Adds endpoint extensions; enabled reasoning and canonical model/messages/limit/stream fields win. */\n readonly extraBody?: Readonly<Record<string, unknown>>\n /** Opts into a compatible endpoint's `{ reasoning: { effort } }` request extension. */\n readonly reasoningEffortFormat?: 'reasoning-object'\n /** Customizes safe error metadata for a branded OpenAI-compatible endpoint. */\n readonly providerIdentity?: OpenAiProviderIdentity\n readonly apiKey: Redacted.Redacted<string>\n}\n\ntype OpenAiTextContentPart = {\n readonly type: 'text'\n readonly text: string\n}\n\ntype OpenAiImageContentPart = {\n readonly type: 'image_url'\n readonly image_url: {\n readonly url: string\n }\n}\n\ntype OpenAiUserContent = string | ReadonlyArray<OpenAiTextContentPart | OpenAiImageContentPart>\n\ntype OpenAiToolCall = {\n readonly id: string\n readonly type: 'function'\n readonly function: {\n readonly name: string\n readonly arguments: string\n }\n}\n\ntype OpenAiMessage =\n | { readonly role: 'system'; readonly content: string }\n | { readonly role: 'user'; readonly content: OpenAiUserContent }\n | {\n readonly role: 'assistant'\n readonly content: string | null\n readonly tool_calls?: ReadonlyArray<OpenAiToolCall>\n }\n | { readonly role: 'tool'; readonly tool_call_id: string; readonly content: string }\n\ntype OpenAiTool = {\n readonly type: 'function'\n readonly function: {\n readonly name: string\n readonly description: string\n readonly parameters: unknown\n }\n}\n\ntype OpenAiRequestBody = {\n readonly model: string\n readonly messages: ReadonlyArray<OpenAiMessage>\n readonly max_completion_tokens?: number\n readonly max_tokens?: number\n readonly stream: false\n readonly reasoning?: {\n readonly effort: AgentReasoningEffort\n }\n readonly tools?: ReadonlyArray<OpenAiTool>\n readonly parallel_tool_calls?: true\n}\n\ntype OpenAiRequestBodyConfig = {\n readonly maxCompletionTokens: number\n readonly completionTokenField?: 'max_completion_tokens' | 'max_tokens'\n readonly extraBody?: Readonly<Record<string, unknown>>\n readonly reasoningEffortFormat?: 'reasoning-object'\n readonly providerName?: string\n}\n\nconst defaultOpenAiProviderIdentity: OpenAiProviderIdentity = {\n id: 'openai',\n name: 'OpenAI'\n}\n\nclass OpenAiFunctionResponse extends Schema.Class<OpenAiFunctionResponse>('OpenAiFunctionResponse')(\n {\n name: Schema.String,\n arguments: Schema.String\n }\n) {}\n\nclass OpenAiToolCallResponse extends Schema.Class<OpenAiToolCallResponse>('OpenAiToolCallResponse')(\n {\n id: Schema.String,\n type: Schema.Literals(['function']),\n function: OpenAiFunctionResponse\n }\n) {}\n\nclass OpenAiMessageResponse extends Schema.Class<OpenAiMessageResponse>('OpenAiMessageResponse')({\n content: Schema.NullOr(Schema.String),\n tool_calls: Schema.optional(Schema.Array(OpenAiToolCallResponse))\n}) {}\n\nclass OpenAiChoiceResponse extends Schema.Class<OpenAiChoiceResponse>('OpenAiChoiceResponse')({\n message: OpenAiMessageResponse,\n finish_reason: Schema.optional(Schema.NullOr(Schema.String))\n}) {}\n\nclass OpenAiPromptTokensDetails extends Schema.Class<OpenAiPromptTokensDetails>(\n 'OpenAiPromptTokensDetails'\n)({\n cached_tokens: Schema.optional(Schema.Number)\n}) {}\n\nclass OpenAiCompletionTokensDetails extends Schema.Class<OpenAiCompletionTokensDetails>(\n 'OpenAiCompletionTokensDetails'\n)({\n reasoning_tokens: Schema.optional(Schema.Number)\n}) {}\n\nclass OpenAiUsageResponse extends Schema.Class<OpenAiUsageResponse>('OpenAiUsageResponse')({\n prompt_tokens: Schema.Number,\n completion_tokens: Schema.Number,\n prompt_tokens_details: Schema.optional(OpenAiPromptTokensDetails),\n completion_tokens_details: Schema.optional(OpenAiCompletionTokensDetails)\n}) {}\n\nclass OpenAiChatCompletionResponse extends Schema.Class<OpenAiChatCompletionResponse>(\n 'OpenAiChatCompletionResponse'\n)({\n choices: Schema.Array(OpenAiChoiceResponse),\n usage: Schema.optional(OpenAiUsageResponse)\n}) {}\n\nclass OpenAiConfig extends Context.Service<OpenAiConfig, OpenAiProviderConfig>()(\n '@app/OpenAiConfig'\n) {}\n\nconst OpenAiConfigLayer = Layer.effect(\n OpenAiConfig,\n Effect.gen(function* () {\n const apiKey = yield* Config.redacted('OPENAI_API_KEY')\n const maxCompletionTokens = yield* Config.int('OPENAI_MAX_COMPLETION_TOKENS')\n return { apiKey, maxCompletionTokens }\n }).pipe(\n Effect.mapError(\n () =>\n new LLMError({\n cause: 'provider_error',\n message: 'OpenAI provider environment configuration missing',\n retryable: false\n })\n )\n )\n)\n\nconst unknownToMessage = (error: unknown) =>\n error instanceof Error ? error.message : String(error)\n\nconst encodeJsonString = (value: unknown, message: string) =>\n Schema.encodeUnknownEffect(Schema.UnknownFromJsonString)(value).pipe(\n Effect.mapError(\n error =>\n new LLMError({\n cause: 'provider_error',\n message: `${message}: ${unknownToMessage(error)}`,\n retryable: false\n })\n )\n )\n\nconst decodeJsonString = (raw: string, message: string) =>\n Schema.decodeUnknownEffect(Schema.UnknownFromJsonString)(raw).pipe(\n Effect.mapError(\n error =>\n new LLMError({\n cause: 'invalid_response',\n message: `${message}: ${unknownToMessage(error)}`,\n retryable: false\n })\n )\n )\n\nconst unsupportedContentError = (contentType: string, providerName: string) =>\n new LLMError({\n cause: 'provider_error',\n message: `${contentType} content is not supported by the ${providerName} provider yet`,\n retryable: false\n })\n\nconst textDocumentToOpenAiPart = (\n part: Extract<ContentPart, { readonly _tag: 'Document' }>,\n providerName: string\n) =>\n attachmentSourceText(part.source).pipe(\n Effect.mapError(() => unsupportedContentError('Invalid document text', providerName)),\n Effect.flatMap(\n Option.match({\n onNone: () =>\n Effect.fail(unsupportedContentError('Unresolved document source', providerName)),\n onSome: text => {\n const block: OpenAiTextContentPart = {\n type: 'text',\n text: `Document: ${part.title ?? part.filename}\\n\\n${text}`\n }\n\n return Effect.succeed(block)\n }\n })\n )\n )\n\nconst contentPartToUserPart = (\n part: ContentPart,\n providerName: string\n): Effect.Effect<OpenAiTextContentPart | OpenAiImageContentPart, LLMError> => {\n switch (part._tag) {\n case 'Text':\n return Effect.succeed({ type: 'text', text: part.text })\n case 'Image':\n return Option.match(attachmentSourceUrl(part.source, part.mimeType), {\n onNone: () => Effect.fail(unsupportedContentError('Unresolved image source', providerName)),\n onSome: url => Effect.succeed({ type: 'image_url', image_url: { url } })\n })\n case 'Document':\n return isTextDocumentMimeType(part.mimeType)\n ? textDocumentToOpenAiPart(part, providerName)\n : Effect.fail(unsupportedContentError('Document', providerName))\n case 'Audio':\n return Effect.fail(unsupportedContentError('Audio', providerName))\n }\n}\n\nconst contentToUserContent = (\n content: Content,\n providerName: string\n): Effect.Effect<OpenAiUserContent, LLMError> =>\n typeof content === 'string'\n ? Effect.succeed(content)\n : Effect.forEach(content, part => contentPartToUserPart(part, providerName))\n\nconst contentPartToText = (\n part: ContentPart,\n owner: string,\n providerName: string\n): Effect.Effect<string, LLMError> => {\n switch (part._tag) {\n case 'Text':\n return Effect.succeed(part.text)\n case 'Image':\n return Effect.fail(unsupportedContentError(`${owner} image`, providerName))\n case 'Document':\n return Effect.fail(unsupportedContentError(`${owner} document`, providerName))\n case 'Audio':\n return Effect.fail(unsupportedContentError(`${owner} audio`, providerName))\n }\n}\n\nconst contentToText = (\n content: Content,\n owner: string,\n providerName: string\n): Effect.Effect<string, LLMError> =>\n typeof content === 'string'\n ? Effect.succeed(content)\n : Effect.forEach(content, part => contentPartToText(part, owner, providerName)).pipe(\n Effect.map(textParts => textParts.join('\\n'))\n )\n\nconst serializeToolArguments = (params: unknown, providerName: string) =>\n encodeJsonString(params, `Could not serialize ${providerName} tool arguments`)\n\nconst toolCallToOpenAiToolCall = (\n call: ToolCall,\n providerName: string\n): Effect.Effect<OpenAiToolCall, LLMError> =>\n Effect.gen(function* () {\n return {\n id: call.id,\n type: 'function',\n function: {\n name: call.name,\n arguments: yield* serializeToolArguments(call.params, providerName)\n }\n }\n })\n\nconst toOpenAiMessage = (\n message: AgentMessage,\n providerName: string\n): Effect.Effect<OpenAiMessage, LLMError> =>\n Effect.gen(function* () {\n switch (message._tag) {\n case 'User':\n return {\n role: 'user',\n content: yield* contentToUserContent(\n prependMessageContextToContent(message.content, messageContextText(message)),\n providerName\n )\n }\n case 'Assistant': {\n const content = prependMessageContextToContent(\n assistantContent(message),\n messageContextText(message)\n )\n const toolCalls = yield* Effect.forEach(assistantHostToolCalls(message), call =>\n toolCallToOpenAiToolCall(call, providerName)\n )\n\n if (toolCalls.length > 0) {\n return {\n role: 'assistant',\n content: yield* contentToText(content, 'Assistant', providerName),\n tool_calls: toolCalls\n }\n }\n\n return {\n role: 'assistant',\n content: yield* contentToText(content, 'Assistant', providerName)\n }\n }\n case 'ToolResult':\n return {\n role: 'tool',\n tool_call_id: message.toolCallId,\n content: yield* contentToText(\n prependMessageContextToContent(message.content, messageContextText(message)),\n 'Tool result',\n providerName\n )\n }\n }\n })\n\nconst toOpenAiTool = (tool: ToolDef): OpenAiTool => ({\n type: 'function',\n function: {\n name: tool.name,\n description: tool.description,\n parameters: tool.parameters\n }\n})\n\nexport const toOpenAiRequestBody = (\n request: LLMRequest,\n config: OpenAiRequestBodyConfig\n): Effect.Effect<OpenAiRequestBody, LLMError> =>\n Effect.gen(function* () {\n const providerName = config.providerName ?? defaultOpenAiProviderIdentity.name\n\n if (!Number.isSafeInteger(config.maxCompletionTokens) || config.maxCompletionTokens <= 0) {\n return yield* Effect.fail(\n new LLMError({\n cause: 'validation_error',\n message: `${providerName} maxCompletionTokens must be a positive safe integer`,\n retryable: false\n })\n )\n }\n\n yield* validateProviderTranscript(request.messages)\n const systemMessage: OpenAiMessage = { role: 'system', content: request.systemPrompt }\n const requestMessages = yield* Effect.forEach(request.messages, message =>\n toOpenAiMessage(message, providerName)\n )\n const messages = [systemMessage, ...requestMessages]\n\n const completionTokenLimit =\n config.completionTokenField === 'max_tokens'\n ? { max_tokens: config.maxCompletionTokens }\n : { max_completion_tokens: config.maxCompletionTokens }\n const reasoning =\n config.reasoningEffortFormat === 'reasoning-object' && request.reasoningEffort !== undefined\n ? { reasoning: { effort: request.reasoningEffort } }\n : {}\n const body: OpenAiRequestBody = {\n ...config.extraBody,\n ...reasoning,\n model: request.model,\n messages,\n ...completionTokenLimit,\n stream: false\n }\n\n if (request.tools.length === 0) {\n return body\n }\n\n return {\n ...body,\n tools: request.tools.map(toOpenAiTool),\n parallel_tool_calls: true\n }\n })\n\nconst parseToolArguments = (raw: string, providerName: string) =>\n decodeJsonString(raw, `Invalid ${providerName} tool arguments JSON`)\n\nconst toLlmEvents = (\n choice: OpenAiChoiceResponse,\n providerIdentity: OpenAiProviderIdentity\n): Effect.Effect<ReadonlyArray<LLMEvent>, LLMError> =>\n Effect.gen(function* () {\n if (choice.finish_reason === 'length' || choice.finish_reason === 'content_filter') {\n return yield* Effect.fail(\n new LLMError({\n cause: 'invalid_response',\n message: `${providerIdentity.name} response stopped with ${choice.finish_reason}`,\n retryable: false,\n provider: providerErrorInfo({\n provider: providerIdentity.id,\n kind: 'invalid_response',\n providerCode: choice.finish_reason\n })\n })\n )\n }\n\n const content = choice.message.content ?? ''\n const textEvents = content.length > 0 ? [LLMTextDelta.make({ text: content })] : []\n const toolCallEvents = yield* Effect.forEach(choice.message.tool_calls ?? [], call =>\n parseToolArguments(call.function.arguments, providerIdentity.name).pipe(\n Effect.map(params =>\n LLMToolCall.make({\n call: ToolCall.make({\n id: call.id,\n name: call.function.name,\n params\n })\n })\n )\n )\n )\n\n if (toolCallEvents.length > 0) {\n return [...textEvents, ...toolCallEvents, LLMDone.make({ stopReason: 'tool_use' })]\n }\n\n return [...textEvents, LLMDone.make({ stopReason: 'stop' })]\n })\n\nconst toAgentUsage = (usage: OpenAiUsageResponse) =>\n AgentUsage.make({\n input: AgentInputUsage.make({\n total: usage.prompt_tokens,\n uncached: usage.prompt_tokens - (usage.prompt_tokens_details?.cached_tokens ?? 0),\n cacheRead: usage.prompt_tokens_details?.cached_tokens\n }),\n output: AgentOutputUsage.make({\n total: usage.completion_tokens,\n reasoning: usage.completion_tokens_details?.reasoning_tokens,\n text: usage.completion_tokens - (usage.completion_tokens_details?.reasoning_tokens ?? 0)\n })\n })\n\nconst toHttpClientLlmError =\n (providerIdentity: OpenAiProviderIdentity, retryable: boolean) =>\n (error: HttpClientError.HttpClientError) =>\n new LLMError({\n cause: 'provider_error',\n message: `${providerIdentity.name} request failed: ${error.message}`,\n retryable,\n provider: providerErrorInfo({\n provider: providerIdentity.id,\n kind: retryable ? 'network' : 'unknown'\n })\n })\n\nconst parseOpenAiResponseJson = (\n response: HttpClientResponse.HttpClientResponse,\n providerName: string\n): Effect.Effect<unknown, LLMError> =>\n response.json.pipe(\n Effect.mapError(\n error =>\n new LLMError({\n cause: 'invalid_response',\n message: `Could not parse ${providerName} response JSON: ${error.message}`,\n retryable: false\n })\n )\n )\n\nconst sendOpenAiRequest = (\n config: OpenAiProviderConfig,\n request: LLMRequest,\n client: HttpClient.HttpClient\n): Effect.Effect<ReadonlyArray<LLMEvent>, LLMError> =>\n Effect.gen(function* () {\n const providerIdentity = config.providerIdentity ?? defaultOpenAiProviderIdentity\n const body = yield* toOpenAiRequestBody(request, {\n maxCompletionTokens: config.maxCompletionTokens,\n providerName: providerIdentity.name,\n ...(config.completionTokenField === undefined\n ? {}\n : { completionTokenField: config.completionTokenField }),\n ...(config.extraBody === undefined ? {} : { extraBody: config.extraBody }),\n ...(config.reasoningEffortFormat === undefined\n ? {}\n : { reasoningEffortFormat: config.reasoningEffortFormat })\n })\n // Replayed transcripts can carry lone surrogates; harden the lowered\n // body so one bad historical string cannot poison every model call.\n const serializedBody = yield* encodeJsonString(\n replaceLoneSurrogatesDeep(body),\n `Could not serialize ${providerIdentity.name} request`\n )\n\n const httpRequest = HttpClientRequest.post(\n config.chatCompletionsUrl ?? 'https://api.openai.com/v1/chat/completions'\n ).pipe(\n HttpClientRequest.setHeaders({\n ...config.extraHeaders,\n accept: 'application/json',\n authorization: `Bearer ${Redacted.value(config.apiKey)}`,\n 'content-type': 'application/json'\n }),\n HttpClientRequest.bodyText(serializedBody, 'application/json')\n )\n const response = yield* client\n .execute(httpRequest)\n .pipe(Effect.mapError(toHttpClientLlmError(providerIdentity, true)))\n\n if (response.status < 200 || response.status >= 300) {\n const errorText = yield* response.text.pipe(\n Effect.mapError(\n error =>\n new LLMError({\n cause: 'provider_error',\n message: `Could not read ${providerIdentity.name} error body: ${error.message}`,\n retryable: false\n })\n )\n )\n\n const provider = classifyProviderFailure({\n provider: providerIdentity.id,\n status: response.status,\n headers: response.headers,\n body: errorText\n })\n\n return yield* Effect.fail(\n new LLMError({\n cause: providerFailureCause(provider.kind),\n message: `${providerIdentity.name} returned ${response.status}`,\n retryable: providerFailureRetryable(provider.kind),\n provider\n })\n )\n }\n\n const json = yield* parseOpenAiResponseJson(response, providerIdentity.name)\n const parsed = yield* Schema.decodeUnknownEffect(OpenAiChatCompletionResponse)(json).pipe(\n Effect.mapError(\n error =>\n new LLMError({\n cause: 'invalid_response',\n message: `Invalid ${providerIdentity.name} response: ${unknownToMessage(error)}`,\n retryable: false\n })\n )\n )\n const choice = parsed.choices[0]\n\n if (choice === undefined) {\n return yield* Effect.fail(\n new LLMError({\n cause: 'invalid_response',\n message: `${providerIdentity.name} response contained no choices`,\n retryable: false\n })\n )\n }\n\n const events = yield* toLlmEvents(choice, providerIdentity)\n\n if (parsed.usage === undefined) {\n return events\n }\n\n return [...events, LLMUsage.make({ usage: toAgentUsage(parsed.usage) })]\n }).pipe(\n Effect.withSpan(`${config.providerIdentity?.id ?? defaultOpenAiProviderIdentity.id}.stream`)\n )\n\nexport const makeOpenAiProviderLayer = (config: OpenAiProviderConfig) =>\n Layer.effect(\n LLMProvider,\n Effect.gen(function* () {\n const client = yield* HttpClient.HttpClient\n\n return LLMProvider.of({\n stream: request =>\n Stream.fromEffect(sendOpenAiRequest(config, request, client)).pipe(\n Stream.flatMap(events => Stream.fromIterable(events))\n )\n })\n })\n )\n\nexport const OpenAiProviderLayer = Layer.effect(\n LLMProvider,\n Effect.gen(function* () {\n const config = yield* OpenAiConfig\n const client = yield* HttpClient.HttpClient\n\n return LLMProvider.of({\n stream: request =>\n Stream.fromEffect(sendOpenAiRequest(config, request, client)).pipe(\n Stream.flatMap(events => Stream.fromIterable(events))\n )\n })\n })\n).pipe(Layer.provide(Layer.mergeAll(OpenAiConfigLayer, FetchHttpClient.layer)))\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-gateway-provider.d.mts","names":[],"sources":["../../../src/providers/vercel/ai-gateway-provider.ts"],"mappings":";;;;cAKa,yBAAA;AAAA,cACA,iCAAA;AAAA,KAED,2BAAA;AAAA,KAEA,6BAAA;EAAA,SACD,KAAA,GAAQ,aAAA;EAAA,SACR,IAAA,GAAO,aAAA;EAAA,SACP,IAAA,GAAO,2BAAA;AAAA;AAAA,KAGN,6BAAA;8EAED,MAAA,EAAQ,QAAA,CAAS,QAAA;EAAA,SACjB,mBAAA;EAAA,SACA,cAAA,GAAiB,aAAA;EAAA,SACjB,OAAA,GAAU,6BAAA;EAAA,SACV,YAAA,GAAe,QAAA,CAAS,MAAA,mBAdI;EAAA,SAgB5B,kBAAA;AAAA;AAAA,cAaE,gCAAA,GAAoC,MAAA,EAAQ,6BAAA,KAA6B,KAAA,CAAA,KAAA,gCAAA,WAAA,mDAAA,UAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"ai-gateway-provider.d.mts","names":[],"sources":["../../../src/providers/vercel/ai-gateway-provider.ts"],"mappings":";;;;cAKa,yBAAA;AAAA,cACA,iCAAA;AAAA,KAED,2BAAA;AAAA,KAEA,6BAAA;EAAA,SACD,KAAA,GAAQ,aAAA;EAAA,SACR,IAAA,GAAO,aAAA;EAAA,SACP,IAAA,GAAO,2BAAA;AAAA;AAAA,KAGN,6BAAA;8EAED,MAAA,EAAQ,QAAA,CAAS,QAAA;EAAA,SACjB,mBAAA;EAAA,SACA,cAAA,GAAiB,aAAA;EAAA,SACjB,OAAA,GAAU,6BAAA;EAAA,SACV,YAAA,GAAe,QAAA,CAAS,MAAA,mBAdI;EAAA,SAgB5B,kBAAA;AAAA;AAAA,cAaE,gCAAA,GAAoC,MAAA,EAAQ,6BAAA,KAA6B,KAAA,CAAA,KAAA,gCAAA,WAAA,mDAAA,UAAA;AAAA,cA8BzE,4BAAA,EAA4B,KAAA,CAAA,KAAA,gCAAA,WAAA,EAAA,QAAA"}
|
|
@@ -17,6 +17,7 @@ const makeVercelAiGatewayProviderLayer = (config) => makeOpenAiProviderLayer({
|
|
|
17
17
|
apiKey: config.apiKey,
|
|
18
18
|
maxCompletionTokens: config.maxCompletionTokens,
|
|
19
19
|
completionTokenField: "max_tokens",
|
|
20
|
+
reasoningEffortFormat: "reasoning-object",
|
|
20
21
|
chatCompletionsUrl: config.chatCompletionsUrl ?? "https://ai-gateway.vercel.sh/v1/chat/completions",
|
|
21
22
|
providerIdentity: vercelAiGatewayProviderIdentity,
|
|
22
23
|
extraBody: gatewayExtraBody(config),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ai-gateway-provider.mjs","names":[],"sources":["../../../src/providers/vercel/ai-gateway-provider.ts"],"sourcesContent":["import { Config, Effect, Layer, type Redacted } from 'effect'\nimport { FetchHttpClient } from 'effect/unstable/http'\nimport { LLMError } from '@yolk-sdk/agent/loop'\nimport { makeOpenAiProviderLayer } from '../openai/provider.ts'\n\nexport const vercelAiGatewayProviderId = 'vercel_ai_gateway'\nexport const vercelAiGatewayChatCompletionsUrl = 'https://ai-gateway.vercel.sh/v1/chat/completions'\n\nexport type VercelAiGatewayProviderSort = 'cost' | 'ttft' | 'tps'\n\nexport type VercelAiGatewayRoutingOptions = {\n readonly order?: ReadonlyArray<string>\n readonly only?: ReadonlyArray<string>\n readonly sort?: VercelAiGatewayProviderSort\n}\n\nexport type VercelAiGatewayProviderConfig = {\n /** Accepts either an AI Gateway API key or a Vercel OIDC token. */\n readonly apiKey: Redacted.Redacted<string>\n readonly maxCompletionTokens: number\n readonly fallbackModels?: ReadonlyArray<string>\n readonly routing?: VercelAiGatewayRoutingOptions\n readonly extraHeaders?: Readonly<Record<string, string>>\n /** Override only with a trusted proxy because the bearer credential is sent to this URL. */\n readonly chatCompletionsUrl?: string\n}\n\nconst vercelAiGatewayProviderIdentity = {\n id: vercelAiGatewayProviderId,\n name: 'Vercel AI Gateway'\n}\n\nconst gatewayExtraBody = (config: VercelAiGatewayProviderConfig) => ({\n ...(config.fallbackModels === undefined ? {} : { models: config.fallbackModels }),\n ...(config.routing === undefined ? {} : { providerOptions: { gateway: config.routing } })\n})\n\nexport const makeVercelAiGatewayProviderLayer = (config: VercelAiGatewayProviderConfig) =>\n makeOpenAiProviderLayer({\n apiKey: config.apiKey,\n maxCompletionTokens: config.maxCompletionTokens,\n completionTokenField: 'max_tokens',\n chatCompletionsUrl: config.chatCompletionsUrl ?? vercelAiGatewayChatCompletionsUrl,\n providerIdentity: vercelAiGatewayProviderIdentity,\n extraBody: gatewayExtraBody(config),\n ...(config.extraHeaders === undefined ? {} : { extraHeaders: config.extraHeaders })\n })\n\nconst vercelAiGatewayEnvironmentConfig = Effect.gen(function* () {\n const apiKey = yield* Config.redacted('AI_GATEWAY_API_KEY').pipe(\n Config.orElse(() => Config.redacted('VERCEL_OIDC_TOKEN'))\n )\n const maxCompletionTokens = yield* Config.int('AI_GATEWAY_MAX_COMPLETION_TOKENS')\n\n return { apiKey, maxCompletionTokens }\n}).pipe(\n Effect.mapError(\n () =>\n new LLMError({\n cause: 'provider_error',\n message: 'Vercel AI Gateway environment configuration missing',\n retryable: false\n })\n )\n)\n\nexport const VercelAiGatewayProviderLayer = Layer.unwrap(\n vercelAiGatewayEnvironmentConfig.pipe(Effect.map(makeVercelAiGatewayProviderLayer))\n).pipe(Layer.provide(FetchHttpClient.layer))\n"],"mappings":";;;;;AAKA,MAAa,4BAA4B;AACzC,MAAa,oCAAoC;AAqBjD,MAAM,kCAAkC;CACtC,IAAI;CACJ,MAAM;AACR;AAEA,MAAM,oBAAoB,YAA2C;CACnE,GAAI,OAAO,mBAAmB,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ,OAAO,eAAe;CAC/E,GAAI,OAAO,YAAY,KAAA,IAAY,CAAC,IAAI,EAAE,iBAAiB,EAAE,SAAS,OAAO,QAAQ,EAAE;AACzF;AAEA,MAAa,oCAAoC,WAC/C,wBAAwB;CACtB,QAAQ,OAAO;CACf,qBAAqB,OAAO;CAC5B,sBAAsB;CACtB,oBAAoB,OAAO,sBAAA;CAC3B,kBAAkB;CAClB,WAAW,iBAAiB,MAAM;CAClC,GAAI,OAAO,iBAAiB,KAAA,IAAY,CAAC,IAAI,EAAE,cAAc,OAAO,aAAa;AACnF,CAAC;AAEH,MAAM,mCAAmC,OAAO,IAAI,aAAa;CAM/D,OAAO;EAAE,QAAA,OALa,OAAO,SAAS,oBAAoB,EAAE,KAC1D,OAAO,aAAa,OAAO,SAAS,mBAAmB,CAAC,CAC1D;EAGiB,qBAAA,OAFkB,OAAO,IAAI,kCAAkC;CAE3C;AACvC,CAAC,EAAE,KACD,OAAO,eAEH,IAAI,SAAS;CACX,OAAO;CACP,SAAS;CACT,WAAW;AACb,CAAC,CACL,CACF;AAEA,MAAa,+BAA+B,MAAM,OAChD,iCAAiC,KAAK,OAAO,IAAI,gCAAgC,CAAC,CACpF,EAAE,KAAK,MAAM,QAAQ,gBAAgB,KAAK,CAAC"}
|
|
1
|
+
{"version":3,"file":"ai-gateway-provider.mjs","names":[],"sources":["../../../src/providers/vercel/ai-gateway-provider.ts"],"sourcesContent":["import { Config, Effect, Layer, type Redacted } from 'effect'\nimport { FetchHttpClient } from 'effect/unstable/http'\nimport { LLMError } from '@yolk-sdk/agent/loop'\nimport { makeOpenAiProviderLayer } from '../openai/provider.ts'\n\nexport const vercelAiGatewayProviderId = 'vercel_ai_gateway'\nexport const vercelAiGatewayChatCompletionsUrl = 'https://ai-gateway.vercel.sh/v1/chat/completions'\n\nexport type VercelAiGatewayProviderSort = 'cost' | 'ttft' | 'tps'\n\nexport type VercelAiGatewayRoutingOptions = {\n readonly order?: ReadonlyArray<string>\n readonly only?: ReadonlyArray<string>\n readonly sort?: VercelAiGatewayProviderSort\n}\n\nexport type VercelAiGatewayProviderConfig = {\n /** Accepts either an AI Gateway API key or a Vercel OIDC token. */\n readonly apiKey: Redacted.Redacted<string>\n readonly maxCompletionTokens: number\n readonly fallbackModels?: ReadonlyArray<string>\n readonly routing?: VercelAiGatewayRoutingOptions\n readonly extraHeaders?: Readonly<Record<string, string>>\n /** Override only with a trusted proxy because the bearer credential is sent to this URL. */\n readonly chatCompletionsUrl?: string\n}\n\nconst vercelAiGatewayProviderIdentity = {\n id: vercelAiGatewayProviderId,\n name: 'Vercel AI Gateway'\n}\n\nconst gatewayExtraBody = (config: VercelAiGatewayProviderConfig) => ({\n ...(config.fallbackModels === undefined ? {} : { models: config.fallbackModels }),\n ...(config.routing === undefined ? {} : { providerOptions: { gateway: config.routing } })\n})\n\nexport const makeVercelAiGatewayProviderLayer = (config: VercelAiGatewayProviderConfig) =>\n makeOpenAiProviderLayer({\n apiKey: config.apiKey,\n maxCompletionTokens: config.maxCompletionTokens,\n completionTokenField: 'max_tokens',\n reasoningEffortFormat: 'reasoning-object',\n chatCompletionsUrl: config.chatCompletionsUrl ?? vercelAiGatewayChatCompletionsUrl,\n providerIdentity: vercelAiGatewayProviderIdentity,\n extraBody: gatewayExtraBody(config),\n ...(config.extraHeaders === undefined ? {} : { extraHeaders: config.extraHeaders })\n })\n\nconst vercelAiGatewayEnvironmentConfig = Effect.gen(function* () {\n const apiKey = yield* Config.redacted('AI_GATEWAY_API_KEY').pipe(\n Config.orElse(() => Config.redacted('VERCEL_OIDC_TOKEN'))\n )\n const maxCompletionTokens = yield* Config.int('AI_GATEWAY_MAX_COMPLETION_TOKENS')\n\n return { apiKey, maxCompletionTokens }\n}).pipe(\n Effect.mapError(\n () =>\n new LLMError({\n cause: 'provider_error',\n message: 'Vercel AI Gateway environment configuration missing',\n retryable: false\n })\n )\n)\n\nexport const VercelAiGatewayProviderLayer = Layer.unwrap(\n vercelAiGatewayEnvironmentConfig.pipe(Effect.map(makeVercelAiGatewayProviderLayer))\n).pipe(Layer.provide(FetchHttpClient.layer))\n"],"mappings":";;;;;AAKA,MAAa,4BAA4B;AACzC,MAAa,oCAAoC;AAqBjD,MAAM,kCAAkC;CACtC,IAAI;CACJ,MAAM;AACR;AAEA,MAAM,oBAAoB,YAA2C;CACnE,GAAI,OAAO,mBAAmB,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ,OAAO,eAAe;CAC/E,GAAI,OAAO,YAAY,KAAA,IAAY,CAAC,IAAI,EAAE,iBAAiB,EAAE,SAAS,OAAO,QAAQ,EAAE;AACzF;AAEA,MAAa,oCAAoC,WAC/C,wBAAwB;CACtB,QAAQ,OAAO;CACf,qBAAqB,OAAO;CAC5B,sBAAsB;CACtB,uBAAuB;CACvB,oBAAoB,OAAO,sBAAA;CAC3B,kBAAkB;CAClB,WAAW,iBAAiB,MAAM;CAClC,GAAI,OAAO,iBAAiB,KAAA,IAAY,CAAC,IAAI,EAAE,cAAc,OAAO,aAAa;AACnF,CAAC;AAEH,MAAM,mCAAmC,OAAO,IAAI,aAAa;CAM/D,OAAO;EAAE,QAAA,OALa,OAAO,SAAS,oBAAoB,EAAE,KAC1D,OAAO,aAAa,OAAO,SAAS,mBAAmB,CAAC,CAC1D;EAGiB,qBAAA,OAFkB,OAAO,IAAI,kCAAkC;CAE3C;AACvC,CAAC,EAAE,KACD,OAAO,eAEH,IAAI,SAAS;CACX,OAAO;CACP,SAAS;CACT,WAAW;AACb,CAAC,CACL,CACF;AAEA,MAAa,+BAA+B,MAAM,OAChD,iCAAiC,KAAK,OAAO,IAAI,gCAAgC,CAAC,CACpF,EAAE,KAAK,MAAM,QAAQ,gBAAgB,KAAK,CAAC"}
|
|
@@ -25,9 +25,8 @@ declare const mergeSkillsets: (sources: ReadonlyArray<SkillsetSource>) => Effect
|
|
|
25
25
|
readonly name: string;
|
|
26
26
|
readonly hints: readonly string[];
|
|
27
27
|
readonly template: string;
|
|
28
|
-
readonly description?: string | undefined;
|
|
29
28
|
readonly source?: string | undefined;
|
|
30
|
-
readonly
|
|
29
|
+
readonly description?: string | undefined;
|
|
31
30
|
readonly arguments?: readonly {
|
|
32
31
|
readonly required: boolean;
|
|
33
32
|
readonly name: string;
|
|
@@ -35,6 +34,7 @@ declare const mergeSkillsets: (sources: ReadonlyArray<SkillsetSource>) => Effect
|
|
|
35
34
|
}[] | undefined;
|
|
36
35
|
readonly access?: "read" | "write" | "destructive" | undefined;
|
|
37
36
|
readonly fileRefs?: boolean | undefined;
|
|
37
|
+
readonly location?: string | undefined;
|
|
38
38
|
}[];
|
|
39
39
|
}, SkillsetError, never>;
|
|
40
40
|
//#endregion
|
package/dist/skillset/name.d.mts
CHANGED
|
@@ -3,8 +3,8 @@ import { Effect } from "effect";
|
|
|
3
3
|
|
|
4
4
|
//#region src/skillset/name.d.ts
|
|
5
5
|
declare const isValidSkillsetName: (name: string) => boolean;
|
|
6
|
-
declare const validateSkillsetName: (name: string) => Effect.Effect<
|
|
7
|
-
declare const validateDirectoryName: (expected: string, actual: string) => Effect.Effect<
|
|
6
|
+
declare const validateSkillsetName: (name: string) => Effect.Effect<string, never, never> | Effect.Effect<never, SkillsetError, never>;
|
|
7
|
+
declare const validateDirectoryName: (expected: string, actual: string) => Effect.Effect<string, never, never> | Effect.Effect<never, SkillsetError, never>;
|
|
8
8
|
//#endregion
|
|
9
9
|
export { isValidSkillsetName, validateDirectoryName, validateSkillsetName };
|
|
10
10
|
//# sourceMappingURL=name.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"name.d.mts","names":[],"sources":["../../src/skillset/name.ts"],"mappings":";;;;cAMa,mBAAA,GAAuB,IAAY;AAAA,cAGnC,oBAAA,GAAwB,IAAA,aAAY,MAAA,CAAA,MAAA,
|
|
1
|
+
{"version":3,"file":"name.d.mts","names":[],"sources":["../../src/skillset/name.ts"],"mappings":";;;;cAMa,mBAAA,GAAuB,IAAY;AAAA,cAGnC,oBAAA,GAAwB,IAAA,aAAY,MAAA,CAAA,MAAA,yBAAA,MAAA,CAAA,MAAA,QAAA,aAAA;AAAA,cAUpC,qBAAA,GAAyB,QAAA,UAAkB,MAAA,aAAc,MAAA,CAAA,MAAA,yBAAA,MAAA,CAAA,MAAA,QAAA,aAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yolk-sdk/agent",
|
|
3
|
-
"version": "0.1.0-canary.
|
|
3
|
+
"version": "0.1.0-canary.69",
|
|
4
4
|
"description": "Agent protocol, loop, runtime, Effect-native client, compaction, tools, React, providers, OAuth, skillset, and voice primitives.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
replaceLoneSurrogatesDeep,
|
|
22
22
|
prependMessageContextToContent,
|
|
23
23
|
type AgentMessage,
|
|
24
|
+
type AgentReasoningEffort,
|
|
24
25
|
type Content,
|
|
25
26
|
type ContentPart,
|
|
26
27
|
type ToolDef
|
|
@@ -54,8 +55,10 @@ export type OpenAiProviderConfig = {
|
|
|
54
55
|
/** Selects the compatible endpoint's output-limit parameter. Defaults to `max_completion_tokens`. */
|
|
55
56
|
readonly completionTokenField?: 'max_completion_tokens' | 'max_tokens'
|
|
56
57
|
readonly extraHeaders?: Readonly<Record<string, string>>
|
|
57
|
-
/** Adds
|
|
58
|
+
/** Adds endpoint extensions; enabled reasoning and canonical model/messages/limit/stream fields win. */
|
|
58
59
|
readonly extraBody?: Readonly<Record<string, unknown>>
|
|
60
|
+
/** Opts into a compatible endpoint's `{ reasoning: { effort } }` request extension. */
|
|
61
|
+
readonly reasoningEffortFormat?: 'reasoning-object'
|
|
59
62
|
/** Customizes safe error metadata for a branded OpenAI-compatible endpoint. */
|
|
60
63
|
readonly providerIdentity?: OpenAiProviderIdentity
|
|
61
64
|
readonly apiKey: Redacted.Redacted<string>
|
|
@@ -109,6 +112,9 @@ type OpenAiRequestBody = {
|
|
|
109
112
|
readonly max_completion_tokens?: number
|
|
110
113
|
readonly max_tokens?: number
|
|
111
114
|
readonly stream: false
|
|
115
|
+
readonly reasoning?: {
|
|
116
|
+
readonly effort: AgentReasoningEffort
|
|
117
|
+
}
|
|
112
118
|
readonly tools?: ReadonlyArray<OpenAiTool>
|
|
113
119
|
readonly parallel_tool_calls?: true
|
|
114
120
|
}
|
|
@@ -117,6 +123,7 @@ type OpenAiRequestBodyConfig = {
|
|
|
117
123
|
readonly maxCompletionTokens: number
|
|
118
124
|
readonly completionTokenField?: 'max_completion_tokens' | 'max_tokens'
|
|
119
125
|
readonly extraBody?: Readonly<Record<string, unknown>>
|
|
126
|
+
readonly reasoningEffortFormat?: 'reasoning-object'
|
|
120
127
|
readonly providerName?: string
|
|
121
128
|
}
|
|
122
129
|
|
|
@@ -415,8 +422,13 @@ export const toOpenAiRequestBody = (
|
|
|
415
422
|
config.completionTokenField === 'max_tokens'
|
|
416
423
|
? { max_tokens: config.maxCompletionTokens }
|
|
417
424
|
: { max_completion_tokens: config.maxCompletionTokens }
|
|
425
|
+
const reasoning =
|
|
426
|
+
config.reasoningEffortFormat === 'reasoning-object' && request.reasoningEffort !== undefined
|
|
427
|
+
? { reasoning: { effort: request.reasoningEffort } }
|
|
428
|
+
: {}
|
|
418
429
|
const body: OpenAiRequestBody = {
|
|
419
430
|
...config.extraBody,
|
|
431
|
+
...reasoning,
|
|
420
432
|
model: request.model,
|
|
421
433
|
messages,
|
|
422
434
|
...completionTokenLimit,
|
|
@@ -535,7 +547,10 @@ const sendOpenAiRequest = (
|
|
|
535
547
|
...(config.completionTokenField === undefined
|
|
536
548
|
? {}
|
|
537
549
|
: { completionTokenField: config.completionTokenField }),
|
|
538
|
-
...(config.extraBody === undefined ? {} : { extraBody: config.extraBody })
|
|
550
|
+
...(config.extraBody === undefined ? {} : { extraBody: config.extraBody }),
|
|
551
|
+
...(config.reasoningEffortFormat === undefined
|
|
552
|
+
? {}
|
|
553
|
+
: { reasoningEffortFormat: config.reasoningEffortFormat })
|
|
539
554
|
})
|
|
540
555
|
// Replayed transcripts can carry lone surrogates; harden the lowered
|
|
541
556
|
// body so one bad historical string cannot poison every model call.
|
|
@@ -40,6 +40,7 @@ export const makeVercelAiGatewayProviderLayer = (config: VercelAiGatewayProvider
|
|
|
40
40
|
apiKey: config.apiKey,
|
|
41
41
|
maxCompletionTokens: config.maxCompletionTokens,
|
|
42
42
|
completionTokenField: 'max_tokens',
|
|
43
|
+
reasoningEffortFormat: 'reasoning-object',
|
|
43
44
|
chatCompletionsUrl: config.chatCompletionsUrl ?? vercelAiGatewayChatCompletionsUrl,
|
|
44
45
|
providerIdentity: vercelAiGatewayProviderIdentity,
|
|
45
46
|
extraBody: gatewayExtraBody(config),
|