@effect-uai/anthropic 0.18.0 → 0.19.0-dev-20261110065930
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 +1 -1
- package/dist/anthropicTools.test.mjs +1 -0
- package/dist/anthropicTools.test.mjs.map +1 -1
- package/dist/codec.d.mts +15 -1
- package/dist/codec.d.mts.map +1 -1
- package/dist/codec.mjs +39 -5
- package/dist/codec.mjs.map +1 -1
- package/dist/codec.test.d.mts +1 -0
- package/dist/codec.test.mjs +101 -0
- package/dist/codec.test.mjs.map +1 -0
- package/package.json +4 -4
- package/src/anthropicTools.test.ts +1 -0
- package/src/codec.test.ts +78 -0
- package/src/codec.ts +53 -4
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ const localTool = Tool.make({
|
|
|
10
10
|
name: "get_weather",
|
|
11
11
|
description: "Look up the weather.",
|
|
12
12
|
inputSchema: Tool.fromEffectSchema(Schema.Struct({ city: Schema.String })),
|
|
13
|
+
outputSchema: Schema.String,
|
|
13
14
|
run: ({ city }) => Effect.succeed(`sunny in ${city}`)
|
|
14
15
|
});
|
|
15
16
|
const wireFor = (toolkit) => Result.getOrThrow(renderProviderTools(providerToolsOf(toolkit)));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropicTools.test.mjs","names":["expect"],"sources":["../src/anthropicTools.test.ts"],"sourcesContent":["import { Effect, Option, Result, Schema } from \"effect\"\nimport * as Tool from \"@effect-uai/core/Tool\"\nimport { descriptorsOf, providerToolsOf } from \"@effect-uai/core/Tool\"\nimport { describe, expect, it } from \"vitest\"\nimport { codeExecutionTool, renderProviderTools, webSearchTool } from \"./AnthropicTools.js\"\nimport { accumulatorToTurn, buildRequestBody, emptyAccumulator } from \"./codec.js\"\nimport { KnownProviderEvent, applyEvent } from \"./streamEvents.js\"\n\nconst localTool = Tool.make({\n name: \"get_weather\",\n description: \"Look up the weather.\",\n inputSchema: Tool.fromEffectSchema(Schema.Struct({ city: Schema.String })),\n run: ({ city }) => Effect.succeed(`sunny in ${city}`),\n})\n\nconst wireFor = (toolkit: Record<string, Tool.AnyTool>) =>\n Result.getOrThrow(renderProviderTools(providerToolsOf(toolkit)))\n\nconst functionWire = (toolkit: Record<string, Tool.AnyTool>) =>\n descriptorsOf(toolkit).map((t) => ({\n name: t.name,\n description: t.description,\n input_schema: t.inputSchema,\n }))\n\nconst buildWith = (\n toolkit: Record<string, Tool.AnyTool>,\n toolChoice: Option.Option<Record<string, unknown>> = Option.none(),\n) => {\n const tools = [...functionWire(toolkit), ...wireFor(toolkit)]\n return Result.getOrThrow(\n buildRequestBody({\n model: \"claude-sonnet-4-5\",\n history: [{ type: \"message\", role: \"user\", content: [{ type: \"input_text\", text: \"hi\" }] }],\n maxTokens: 1024,\n temperature: Option.none(),\n topP: Option.none(),\n topK: Option.none(),\n stopSequences: Option.none(),\n thinking: Option.none(),\n tools: tools.length > 0 ? Option.some(tools) : Option.none(),\n toolChoice,\n userId: Option.none(),\n outputConfig: Option.none(),\n cacheControl: Option.none(),\n }),\n )\n}\n\ndescribe(\"Anthropic provider tools\", () => {\n it(\"renders web_search to the native wire entry with options\", () => {\n const wire = wireFor({\n web_search: webSearchTool({ maxUses: 3, allowedDomains: [\"example.com\"] }),\n })\n expect(wire).toEqual([\n {\n type: \"web_search_20250305\",\n name: \"web_search\",\n max_uses: 3,\n allowed_domains: [\"example.com\"],\n },\n ])\n })\n\n it(\"renders code_execution to its native wire entry\", () => {\n expect(wireFor({ code_execution: codeExecutionTool })).toEqual([\n { type: \"code_execution_20250522\", name: \"code_execution\" },\n ])\n })\n\n it(\"emits both a custom tool and the native entry in the same tools array\", () => {\n const body = buildWith({ get_weather: localTool, web_search: webSearchTool() })\n expect(body.tools).toEqual([\n expect.objectContaining({ name: \"get_weather\" }),\n { type: \"web_search_20250305\", name: \"web_search\" },\n ])\n })\n\n it(\"omits tool_choice for a native-only request\", () => {\n const body = buildWith({ web_search: webSearchTool() })\n expect(body.tools).toHaveLength(1)\n expect(body.tool_choice).toBeUndefined()\n })\n\n it(\"fails on a foreign provider tool\", () => {\n const foreign = Tool.provider({\n name: \"google_search\",\n description: \"Gemini grounding.\",\n inputSchema: Tool.noInput,\n provider: \"gemini\",\n config: { kind: \"google_search\" },\n })\n expect(Result.isFailure(renderProviderTools(providerToolsOf({ google_search: foreign })))).toBe(\n true,\n )\n })\n\n it(\"fails on an unrecognized config\", () => {\n const bogus = Tool.provider({\n name: \"mystery\",\n description: \"Unknown Anthropic tool.\",\n inputSchema: Tool.noInput,\n provider: \"anthropic\",\n config: { kind: \"telepathy\" },\n })\n expect(Result.isFailure(renderProviderTools(providerToolsOf({ mystery: bogus })))).toBe(true)\n })\n})\n\ndescribe(\"Anthropic hosted-tool response tolerance\", () => {\n it(\"does not decode a server_tool_use content block as a known event\", () => {\n const serverToolUse = {\n type: \"content_block_start\",\n index: 0,\n content_block: { type: \"server_tool_use\", id: \"srvtoolu_1\", name: \"web_search\", input: {} },\n }\n const decoded = Schema.decodeUnknownResult(KnownProviderEvent)(serverToolUse)\n // Fails to decode → the SSE layer synthesizes `_unknown`, which is ignored.\n expect(Result.isFailure(decoded)).toBe(true)\n })\n\n it(\"builds the text turn when hosted-tool blocks arrive as _unknown\", () => {\n // Sequence: server_tool_use + web_search_tool_result blocks flow through as\n // `_unknown` (no accumulator change); the assistant's text answer is a\n // normal text block and survives into the turn.\n const events = [\n { type: \"_unknown\", raw: { type: \"content_block_start\", content_block: \"server_tool_use\" } },\n { type: \"content_block_start\", index: 1, content_block: { type: \"text\", text: \"\" } },\n {\n type: \"content_block_delta\",\n index: 1,\n delta: { type: \"text_delta\", text: \"Grounded answer.\" },\n },\n { type: \"content_block_stop\", index: 1 },\n { type: \"message_stop\" },\n ] as const\n const acc = events.reduce(applyEvent, emptyAccumulator)\n const turn = accumulatorToTurn(acc)\n expect(turn.items).toEqual([\n {\n type: \"message\",\n role: \"assistant\",\n content: [{ type: \"output_text\", text: \"Grounded answer.\" }],\n },\n ])\n })\n})\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"anthropicTools.test.mjs","names":["expect"],"sources":["../src/anthropicTools.test.ts"],"sourcesContent":["import { Effect, Option, Result, Schema } from \"effect\"\nimport * as Tool from \"@effect-uai/core/Tool\"\nimport { descriptorsOf, providerToolsOf } from \"@effect-uai/core/Tool\"\nimport { describe, expect, it } from \"vitest\"\nimport { codeExecutionTool, renderProviderTools, webSearchTool } from \"./AnthropicTools.js\"\nimport { accumulatorToTurn, buildRequestBody, emptyAccumulator } from \"./codec.js\"\nimport { KnownProviderEvent, applyEvent } from \"./streamEvents.js\"\n\nconst localTool = Tool.make({\n name: \"get_weather\",\n description: \"Look up the weather.\",\n inputSchema: Tool.fromEffectSchema(Schema.Struct({ city: Schema.String })),\n outputSchema: Schema.String,\n run: ({ city }) => Effect.succeed(`sunny in ${city}`),\n})\n\nconst wireFor = (toolkit: Record<string, Tool.AnyTool>) =>\n Result.getOrThrow(renderProviderTools(providerToolsOf(toolkit)))\n\nconst functionWire = (toolkit: Record<string, Tool.AnyTool>) =>\n descriptorsOf(toolkit).map((t) => ({\n name: t.name,\n description: t.description,\n input_schema: t.inputSchema,\n }))\n\nconst buildWith = (\n toolkit: Record<string, Tool.AnyTool>,\n toolChoice: Option.Option<Record<string, unknown>> = Option.none(),\n) => {\n const tools = [...functionWire(toolkit), ...wireFor(toolkit)]\n return Result.getOrThrow(\n buildRequestBody({\n model: \"claude-sonnet-4-5\",\n history: [{ type: \"message\", role: \"user\", content: [{ type: \"input_text\", text: \"hi\" }] }],\n maxTokens: 1024,\n temperature: Option.none(),\n topP: Option.none(),\n topK: Option.none(),\n stopSequences: Option.none(),\n thinking: Option.none(),\n tools: tools.length > 0 ? Option.some(tools) : Option.none(),\n toolChoice,\n userId: Option.none(),\n outputConfig: Option.none(),\n cacheControl: Option.none(),\n }),\n )\n}\n\ndescribe(\"Anthropic provider tools\", () => {\n it(\"renders web_search to the native wire entry with options\", () => {\n const wire = wireFor({\n web_search: webSearchTool({ maxUses: 3, allowedDomains: [\"example.com\"] }),\n })\n expect(wire).toEqual([\n {\n type: \"web_search_20250305\",\n name: \"web_search\",\n max_uses: 3,\n allowed_domains: [\"example.com\"],\n },\n ])\n })\n\n it(\"renders code_execution to its native wire entry\", () => {\n expect(wireFor({ code_execution: codeExecutionTool })).toEqual([\n { type: \"code_execution_20250522\", name: \"code_execution\" },\n ])\n })\n\n it(\"emits both a custom tool and the native entry in the same tools array\", () => {\n const body = buildWith({ get_weather: localTool, web_search: webSearchTool() })\n expect(body.tools).toEqual([\n expect.objectContaining({ name: \"get_weather\" }),\n { type: \"web_search_20250305\", name: \"web_search\" },\n ])\n })\n\n it(\"omits tool_choice for a native-only request\", () => {\n const body = buildWith({ web_search: webSearchTool() })\n expect(body.tools).toHaveLength(1)\n expect(body.tool_choice).toBeUndefined()\n })\n\n it(\"fails on a foreign provider tool\", () => {\n const foreign = Tool.provider({\n name: \"google_search\",\n description: \"Gemini grounding.\",\n inputSchema: Tool.noInput,\n provider: \"gemini\",\n config: { kind: \"google_search\" },\n })\n expect(Result.isFailure(renderProviderTools(providerToolsOf({ google_search: foreign })))).toBe(\n true,\n )\n })\n\n it(\"fails on an unrecognized config\", () => {\n const bogus = Tool.provider({\n name: \"mystery\",\n description: \"Unknown Anthropic tool.\",\n inputSchema: Tool.noInput,\n provider: \"anthropic\",\n config: { kind: \"telepathy\" },\n })\n expect(Result.isFailure(renderProviderTools(providerToolsOf({ mystery: bogus })))).toBe(true)\n })\n})\n\ndescribe(\"Anthropic hosted-tool response tolerance\", () => {\n it(\"does not decode a server_tool_use content block as a known event\", () => {\n const serverToolUse = {\n type: \"content_block_start\",\n index: 0,\n content_block: { type: \"server_tool_use\", id: \"srvtoolu_1\", name: \"web_search\", input: {} },\n }\n const decoded = Schema.decodeUnknownResult(KnownProviderEvent)(serverToolUse)\n // Fails to decode → the SSE layer synthesizes `_unknown`, which is ignored.\n expect(Result.isFailure(decoded)).toBe(true)\n })\n\n it(\"builds the text turn when hosted-tool blocks arrive as _unknown\", () => {\n // Sequence: server_tool_use + web_search_tool_result blocks flow through as\n // `_unknown` (no accumulator change); the assistant's text answer is a\n // normal text block and survives into the turn.\n const events = [\n { type: \"_unknown\", raw: { type: \"content_block_start\", content_block: \"server_tool_use\" } },\n { type: \"content_block_start\", index: 1, content_block: { type: \"text\", text: \"\" } },\n {\n type: \"content_block_delta\",\n index: 1,\n delta: { type: \"text_delta\", text: \"Grounded answer.\" },\n },\n { type: \"content_block_stop\", index: 1 },\n { type: \"message_stop\" },\n ] as const\n const acc = events.reduce(applyEvent, emptyAccumulator)\n const turn = accumulatorToTurn(acc)\n expect(turn.items).toEqual([\n {\n type: \"message\",\n role: \"assistant\",\n content: [{ type: \"output_text\", text: \"Grounded answer.\" }],\n },\n ])\n })\n})\n"],"mappings":"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"}
|
package/dist/codec.d.mts
CHANGED
|
@@ -36,8 +36,22 @@ type RequestTextContent = {
|
|
|
36
36
|
type RequestToolResultContent = {
|
|
37
37
|
readonly type: "tool_result";
|
|
38
38
|
readonly tool_use_id: string;
|
|
39
|
-
readonly content: string
|
|
39
|
+
readonly content: string | ReadonlyArray<RequestToolResultBlock>;
|
|
40
|
+
readonly is_error?: boolean;
|
|
40
41
|
};
|
|
42
|
+
type RequestDocumentContent = {
|
|
43
|
+
readonly type: "document";
|
|
44
|
+
readonly source: {
|
|
45
|
+
readonly type: "url";
|
|
46
|
+
readonly url: string;
|
|
47
|
+
} | {
|
|
48
|
+
readonly type: "base64";
|
|
49
|
+
readonly media_type: string;
|
|
50
|
+
readonly data: string;
|
|
51
|
+
};
|
|
52
|
+
readonly title?: string;
|
|
53
|
+
};
|
|
54
|
+
type RequestToolResultBlock = RequestTextContent | RequestImageContent | RequestDocumentContent;
|
|
41
55
|
type RequestToolUseContent = {
|
|
42
56
|
readonly type: "tool_use";
|
|
43
57
|
readonly id: string;
|
package/dist/codec.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codec.d.mts","names":[],"sources":["../src/codec.ts"],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"codec.d.mts","names":[],"sources":["../src/codec.ts"],"mappings":";;;;;;;cAmCa,kBAAgB,OAAA,gBAAA,OAAA;;;;;;;;;;;;;;;;KAMjB,0BAA0B,iBAAiB;cAEjD,WAAS,OAAA;;;;;;KAMH,mBAAmB,UAAU;KAMpC;WACM;WACA;;KAGN;WACM;WACA;WACA,kBAAkB,cAAc;WAChC;;KAGN;WACM;WACA;aACM;aAAsB;;aACtB;aAAyB;aAA6B;;WAC5D;;KAGN,yBAAyB,qBAAqB,sBAAsB;KAEpE;WACM;WACA;WACA;WACA;;KAGN;WACM;WACA;WACA;;KAGN;WACM;WACA;;KAGN;WACM;WACA;aACM;aAAsB;;aACtB;aAAyB;aAA6B;;;KAGlE,0BAA0B,qBAAqB,2BAA2B;KAE1E,+BACD,qBACA,wBACA,yBACA;KAEC;WACM;WACA,SAAS,cAAc;;KAG7B;WACM;WACA,SAAS,cAAc;;KAG7B,iBAAiB,qBAAqB;KA6P/B;WACD;WACA;;;KAIC;;;;;;;KAQA;WACD;WACA,MAAM;;KAGL;WACD;WACA,UAAU,cAAc;WACxB;WACA;WACA;WACA;WACA;WACA,iBAAiB;WACjB,WAAW;WACX,QAAQ,cAAc;WACtB,cAAc;WACd;aAAsB;;WACtB,gBAAgB;WAChB,gBAAgB;WAChB;;cAGE,mBAAgB;WAClB;WACA,SAAS,cAAc,MAAM;WAC7B;WACA,aAAa,OAAO;WACpB,MAAM,OAAO;WACb,MAAM,OAAO;WACb,eAAe,OAAO,OAAO;WAC7B,UAAU,OAAO,OAAO;WACxB,OAAO,OAAO,OAAO,cAAc;WACnC,YAAY,OAAO,OAAO;WAC1B,QAAQ,OAAO;WACf,cAAc,OAAO,OAAO;WAC5B,cAAc,OAAO,OAAO;MACnC,OAAO,OAAO,aAAa;KA4D1B;WACM,MAAM;WACN;WACA;WACA;WACA;WACA,IAAI,OAAO;WACX,MAAM,OAAO;WACb,cAAc,OAAO;;KAcpB;WACD,QAAQ,SAAS,eAAe;WAChC,YAAY,OAAO;WACnB,OAAO,MAAM;;cAGX,kBAAkB;cAiBlB,aAAU,KAAS,aAAW,eAAe,OAAS,qBAAmB;cAoBzE,kBAAe,KAAS,aAAW,eAAe,iBAAiB;cAGnE,uBAAoB,KAC1B,aAAW,eACH,oBAEZ;cAEU,sBAAmB,KACzB,aAAW,eACH,qBAEZ;cAEU,uBAAoB,KAC1B,aAAW,eACH,sBAEZ;cAEU,gBAAa,KAAS,aAAW,mBAAmB;cAWpD,aAAU,KAAS,aAAW,MAAQ,cAAY;cA+GlD,oBAAiB,KAAS,gBAAc"}
|
package/dist/codec.mjs
CHANGED
|
@@ -2,6 +2,7 @@ import { n as __exportAll } from "./rolldown-runtime-EAsCxrDo.mjs";
|
|
|
2
2
|
import { Array, Match, Option, Order, Result, Schema, pipe } from "effect";
|
|
3
3
|
import { Base64 } from "effect/encoding";
|
|
4
4
|
import { JsonParseError } from "@effect-uai/core/JSONL";
|
|
5
|
+
import * as ToolOutput from "@effect-uai/core/ToolOutput";
|
|
5
6
|
//#region src/codec.ts
|
|
6
7
|
var codec_exports = /* @__PURE__ */ __exportAll({
|
|
7
8
|
WireContentBlock: () => WireContentBlock,
|
|
@@ -67,6 +68,43 @@ const imageSourceToWire = Match.type().pipe(Match.tag("url", (s) => ({
|
|
|
67
68
|
media_type: s.mimeType,
|
|
68
69
|
data: Base64.encode(s.bytes)
|
|
69
70
|
})), Match.exhaustive);
|
|
71
|
+
const fileSourceToWire = Match.type().pipe(Match.tag("url", (s) => ({
|
|
72
|
+
type: "url",
|
|
73
|
+
url: s.url
|
|
74
|
+
})), Match.tag("base64", (s) => ({
|
|
75
|
+
type: "base64",
|
|
76
|
+
media_type: s.mimeType,
|
|
77
|
+
data: s.base64
|
|
78
|
+
})), Match.tag("bytes", (s) => ({
|
|
79
|
+
type: "base64",
|
|
80
|
+
media_type: s.mimeType,
|
|
81
|
+
data: Base64.encode(s.bytes)
|
|
82
|
+
})), Match.exhaustive);
|
|
83
|
+
const toolResultBlock = Match.type().pipe(Match.discriminatorsExhaustive("type")({
|
|
84
|
+
input_text: (p) => ({
|
|
85
|
+
type: "text",
|
|
86
|
+
text: ToolOutput.partText(p)
|
|
87
|
+
}),
|
|
88
|
+
input_json: (p) => ({
|
|
89
|
+
type: "text",
|
|
90
|
+
text: ToolOutput.partText(p)
|
|
91
|
+
}),
|
|
92
|
+
input_image: (p) => ({
|
|
93
|
+
type: "image",
|
|
94
|
+
source: imageSourceToWire(p.source)
|
|
95
|
+
}),
|
|
96
|
+
input_file: (p) => ({
|
|
97
|
+
type: "document",
|
|
98
|
+
source: fileSourceToWire(p.source),
|
|
99
|
+
...p.filename !== void 0 && { title: p.filename }
|
|
100
|
+
})
|
|
101
|
+
}));
|
|
102
|
+
const toolResult = (o) => ({
|
|
103
|
+
type: "tool_result",
|
|
104
|
+
tool_use_id: o.call_id,
|
|
105
|
+
content: Option.getOrElse(ToolOutput.text(o.output), () => o.output.map(toolResultBlock)),
|
|
106
|
+
...o.isError === true && { is_error: true }
|
|
107
|
+
});
|
|
70
108
|
const userContentBlock = (block) => Match.value(block).pipe(Match.discriminatorsExhaustive("type")({
|
|
71
109
|
input_text: (b) => b.text.length === 0 ? Result.failVoid : Result.succeed({
|
|
72
110
|
type: "text",
|
|
@@ -96,11 +134,7 @@ const roleBucket = (item) => Match.value(item).pipe(Match.discriminatorsExhausti
|
|
|
96
134
|
const itemToUserBlocks = (item) => Match.value(item).pipe(Match.discriminatorsExhaustive("type")({
|
|
97
135
|
message: (m) => m.role === "user" ? pipe(m.content, Array.filterMap(userContentBlock)) : [],
|
|
98
136
|
function_call: () => [],
|
|
99
|
-
function_call_output: (o) => [
|
|
100
|
-
type: "tool_result",
|
|
101
|
-
tool_use_id: o.call_id,
|
|
102
|
-
content: o.output
|
|
103
|
-
}],
|
|
137
|
+
function_call_output: (o) => [toolResult(o)],
|
|
104
138
|
reasoning: () => []
|
|
105
139
|
}));
|
|
106
140
|
const itemToAssistantBlocks = (item) => Match.value(item).pipe(Match.discriminatorsExhaustive("type")({
|
package/dist/codec.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codec.mjs","names":["Arr"],"sources":["../src/codec.ts"],"sourcesContent":["import { Array as Arr, Match, Option, Order, Result, Schema, pipe } from \"effect\"\nimport { Base64 } from \"effect/encoding\"\nimport * as Items from \"@effect-uai/core/Items\"\nimport { JsonParseError } from \"@effect-uai/core/JSONL\"\nimport type { Turn } from \"@effect-uai/core/Turn\"\n\n// ---------------------------------------------------------------------------\n// Wire schemas - subset of Anthropic Messages API we consume.\n// Reference: https://platform.claude.com/docs/en/api/messages\n// ---------------------------------------------------------------------------\n\nconst WireTextBlock = Schema.Struct({\n type: Schema.Literal(\"text\"),\n text: Schema.String,\n})\n\nconst WireToolUseBlock = Schema.Struct({\n type: Schema.Literal(\"tool_use\"),\n id: Schema.String,\n name: Schema.String,\n input: Schema.Unknown,\n})\n\nconst WireThinkingBlock = Schema.Struct({\n type: Schema.Literal(\"thinking\"),\n thinking: Schema.String,\n signature: Schema.optional(Schema.String),\n})\n\nconst WireRedactedThinkingBlock = Schema.Struct({\n type: Schema.Literal(\"redacted_thinking\"),\n data: Schema.String,\n})\n\nexport const WireContentBlock = Schema.Union([\n WireTextBlock,\n WireToolUseBlock,\n WireThinkingBlock,\n WireRedactedThinkingBlock,\n])\nexport type WireContentBlock = typeof WireContentBlock.Type\n\nconst WireUsage = Schema.Struct({\n input_tokens: Schema.optional(Schema.Number),\n output_tokens: Schema.optional(Schema.Number),\n cache_creation_input_tokens: Schema.optional(Schema.NullOr(Schema.Number)),\n cache_read_input_tokens: Schema.optional(Schema.NullOr(Schema.Number)),\n})\nexport type WireUsage = typeof WireUsage.Type\n\n// ---------------------------------------------------------------------------\n// History → request body\n// ---------------------------------------------------------------------------\n\ntype RequestTextContent = {\n readonly type: \"text\"\n readonly text: string\n}\n\ntype RequestToolResultContent = {\n readonly type: \"tool_result\"\n readonly tool_use_id: string\n readonly content: string\n}\n\ntype RequestToolUseContent = {\n readonly type: \"tool_use\"\n readonly id: string\n readonly name: string\n readonly input: unknown\n}\n\ntype RequestThinkingContent = {\n readonly type: \"thinking\"\n readonly thinking: string\n readonly signature?: string\n}\n\ntype RequestRedactedThinkingContent = {\n readonly type: \"redacted_thinking\"\n readonly data: string\n}\n\ntype RequestImageContent = {\n readonly type: \"image\"\n readonly source:\n | { readonly type: \"url\"; readonly url: string }\n | { readonly type: \"base64\"; readonly media_type: string; readonly data: string }\n}\n\ntype RequestUserContentBlock = RequestTextContent | RequestToolResultContent | RequestImageContent\n\ntype RequestAssistantContentBlock =\n | RequestTextContent\n | RequestToolUseContent\n | RequestThinkingContent\n | RequestRedactedThinkingContent\n\ntype RequestUserMessage = {\n readonly role: \"user\"\n readonly content: ReadonlyArray<RequestUserContentBlock>\n}\n\ntype RequestAssistantMessage = {\n readonly role: \"assistant\"\n readonly content: ReadonlyArray<RequestAssistantContentBlock>\n}\n\ntype RequestMessage = RequestUserMessage | RequestAssistantMessage\n\nconst blockText = Match.type<Items.ContentBlock>().pipe(\n Match.discriminatorsExhaustive(\"type\")({\n input_text: (b) => b.text,\n input_image: () => \"\",\n output_text: (b) => b.text,\n output_image: () => \"\",\n refusal: (b) => b.text,\n }),\n)\n\nconst messageText = (message: Items.Message): string => message.content.map(blockText).join(\"\")\n\nconst imageSourceToWire = Match.type<Items.InputImage[\"source\"]>().pipe(\n Match.tag(\"url\", (s): RequestImageContent[\"source\"] => ({ type: \"url\", url: s.url })),\n Match.tag(\"base64\", (s): RequestImageContent[\"source\"] => ({\n type: \"base64\",\n media_type: s.mimeType,\n data: s.base64,\n })),\n Match.tag(\"bytes\", (s): RequestImageContent[\"source\"] => ({\n type: \"base64\",\n media_type: s.mimeType,\n data: Base64.encode(s.bytes),\n })),\n Match.exhaustive,\n)\n\nconst userContentBlock = (\n block: Items.ContentBlock,\n): Result.Result<RequestUserContentBlock, void> =>\n Match.value(block).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n input_text: (b) =>\n b.text.length === 0\n ? Result.failVoid\n : Result.succeed({ type: \"text\" as const, text: b.text }),\n input_image: (b) =>\n Result.succeed({ type: \"image\" as const, source: imageSourceToWire(b.source) }),\n // Assistant content; never appears on a user message in practice. Skip.\n output_text: () => Result.failVoid,\n // No assistant-image block here either. Dropped rather than relocated\n // onto a user message, which is the caller's call: `Turn.imagesAsInput`.\n output_image: () => Result.failVoid,\n refusal: () => Result.failVoid,\n }),\n )\n\nconst parseJson = (s: string): Result.Result<unknown, JsonParseError> =>\n Result.try({\n try: () => JSON.parse(s) as unknown,\n catch: (cause) => new JsonParseError({ line: s, cause }),\n })\n\ntype RoleBucket = \"user\" | \"assistant\" | \"system\"\n\nconst roleBucket = (item: Items.HistoryItem): RoleBucket =>\n Match.value(item).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n message: (m) => m.role,\n function_call: () => \"assistant\" as const,\n function_call_output: () => \"user\" as const,\n reasoning: () => \"assistant\" as const,\n }),\n )\n\nconst itemToUserBlocks = (item: Items.HistoryItem): ReadonlyArray<RequestUserContentBlock> =>\n Match.value(item).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n message: (m): ReadonlyArray<RequestUserContentBlock> =>\n m.role === \"user\" ? pipe(m.content, Arr.filterMap(userContentBlock)) : [],\n function_call: (): ReadonlyArray<RequestUserContentBlock> => [],\n function_call_output: (o): ReadonlyArray<RequestUserContentBlock> => [\n { type: \"tool_result\", tool_use_id: o.call_id, content: o.output },\n ],\n reasoning: (): ReadonlyArray<RequestUserContentBlock> => [],\n }),\n )\n\nconst itemToAssistantBlocks = (\n item: Items.HistoryItem,\n): Result.Result<ReadonlyArray<RequestAssistantContentBlock>, JsonParseError> =>\n Match.value(item).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n message: (m): Result.Result<ReadonlyArray<RequestAssistantContentBlock>, JsonParseError> => {\n const text = messageText(m)\n return Result.succeed(\n m.role === \"assistant\" && text.length > 0 ? [{ type: \"text\", text }] : [],\n )\n },\n function_call: (f) =>\n pipe(\n parseJson(f.arguments),\n Result.map((input): ReadonlyArray<RequestAssistantContentBlock> => [\n { type: \"tool_use\", id: f.call_id, name: f.name, input },\n ]),\n ),\n function_call_output: (): Result.Result<\n ReadonlyArray<RequestAssistantContentBlock>,\n JsonParseError\n > => Result.succeed([]),\n reasoning: (r) => {\n const blocks: ReadonlyArray<RequestAssistantContentBlock> =\n r.summary !== undefined\n ? [\n {\n type: \"thinking\",\n thinking: r.summary,\n ...(r.signature !== undefined && { signature: r.signature }),\n },\n ]\n : r.signature !== undefined\n ? [{ type: \"redacted_thinking\", data: r.signature }]\n : []\n return Result.succeed(blocks)\n },\n }),\n )\n\ntype GroupAcc = {\n readonly messages: ReadonlyArray<RequestMessage>\n readonly currentRole: Option.Option<\"user\" | \"assistant\">\n readonly userBuf: ReadonlyArray<RequestUserContentBlock>\n readonly assistantBuf: ReadonlyArray<RequestAssistantContentBlock>\n}\n\nconst flushAcc = (acc: GroupAcc): ReadonlyArray<RequestMessage> =>\n Option.match(acc.currentRole, {\n onNone: () => acc.messages,\n onSome: (role) =>\n role === \"user\" && acc.userBuf.length > 0\n ? [...acc.messages, { role: \"user\", content: acc.userBuf }]\n : role === \"assistant\" && acc.assistantBuf.length > 0\n ? [...acc.messages, { role: \"assistant\", content: acc.assistantBuf }]\n : acc.messages,\n })\n\nconst appendUser = (acc: GroupAcc, blocks: ReadonlyArray<RequestUserContentBlock>): GroupAcc =>\n blocks.length === 0\n ? acc\n : Option.isSome(acc.currentRole) && acc.currentRole.value === \"user\"\n ? { ...acc, userBuf: [...acc.userBuf, ...blocks] }\n : {\n messages: flushAcc(acc),\n currentRole: Option.some(\"user\"),\n userBuf: blocks,\n assistantBuf: [],\n }\n\nconst appendAssistant = (\n acc: GroupAcc,\n blocks: ReadonlyArray<RequestAssistantContentBlock>,\n): GroupAcc =>\n blocks.length === 0\n ? acc\n : Option.isSome(acc.currentRole) && acc.currentRole.value === \"assistant\"\n ? { ...acc, assistantBuf: [...acc.assistantBuf, ...blocks] }\n : {\n messages: flushAcc(acc),\n currentRole: Option.some(\"assistant\"),\n userBuf: [],\n assistantBuf: blocks,\n }\n\nconst groupStep = (\n acc: GroupAcc,\n item: Items.HistoryItem,\n): Result.Result<GroupAcc, JsonParseError> => {\n const bucket = roleBucket(item)\n if (bucket === \"system\") return Result.succeed(acc)\n if (bucket === \"user\") {\n return Result.succeed(appendUser(acc, itemToUserBlocks(item)))\n }\n return pipe(\n itemToAssistantBlocks(item),\n Result.map((blocks) => appendAssistant(acc, blocks)),\n )\n}\n\n/**\n * Group consecutive same-role items into Anthropic-shaped messages.\n * Anthropic requires strict user/assistant alternation; consecutive items\n * from the same role are folded into one message's `content`. Fails if any\n * `function_call.arguments` is not valid JSON, since Anthropic's wire shape\n * requires an object input.\n */\nconst groupedMessages = (\n history: ReadonlyArray<Items.HistoryItem>,\n): Result.Result<ReadonlyArray<RequestMessage>, JsonParseError> => {\n const initial: Result.Result<GroupAcc, JsonParseError> = Result.succeed({\n messages: [],\n currentRole: Option.none(),\n userBuf: [],\n assistantBuf: [],\n })\n return pipe(\n Arr.reduce(history, initial, (acc, item) => Result.flatMap(acc, (a) => groupStep(a, item))),\n Result.map(flushAcc),\n )\n}\n\nconst isSystemMessage = (item: Items.HistoryItem): item is Items.Message =>\n item.type === \"message\" && item.role === \"system\"\n\nconst systemFromHistory = (history: ReadonlyArray<Items.HistoryItem>): Option.Option<string> => {\n const texts = pipe(\n history,\n Arr.filterMap((item) =>\n isSystemMessage(item) ? Result.succeed(messageText(item)) : Result.failVoid,\n ),\n Arr.filter((s) => s.length > 0),\n )\n return texts.length === 0 ? Option.none() : Option.some(texts.join(\"\\n\"))\n}\n\nexport type ThinkingConfig = {\n readonly type: \"enabled\"\n readonly budget_tokens: number\n}\n\n/** Cache lifetime. `1h` writes cost 2x base input; `5m` costs 1.25x. */\nexport type CacheTtl = \"5m\" | \"1h\"\n\n/**\n * Request-level cache breakpoint. Anthropic applies it to the last cacheable\n * block and moves it forward as the conversation grows, so the cached prefix\n * (`tools` → `system` → `messages`) follows the conversation without the\n * caller re-placing anything.\n */\nexport type CacheControl = {\n readonly type: \"ephemeral\"\n readonly ttl?: CacheTtl\n}\n\nexport type RequestBody = {\n readonly model: string\n readonly messages: ReadonlyArray<RequestMessage>\n readonly max_tokens: number\n readonly system?: string\n readonly temperature?: number\n readonly top_p?: number\n readonly top_k?: number\n readonly stop_sequences?: ReadonlyArray<string>\n readonly thinking?: ThinkingConfig\n readonly tools?: ReadonlyArray<Record<string, unknown>>\n readonly tool_choice?: Record<string, unknown>\n readonly metadata?: { readonly user_id: string }\n readonly output_config?: Record<string, unknown>\n readonly cache_control?: CacheControl\n readonly stream: true\n}\n\nexport const buildRequestBody = (params: {\n readonly model: string\n readonly history: ReadonlyArray<Items.HistoryItem>\n readonly maxTokens: number\n readonly temperature: Option.Option<number>\n readonly topP: Option.Option<number>\n readonly topK: Option.Option<number>\n readonly stopSequences: Option.Option<ReadonlyArray<string>>\n readonly thinking: Option.Option<ThinkingConfig>\n readonly tools: Option.Option<ReadonlyArray<Record<string, unknown>>>\n readonly toolChoice: Option.Option<Record<string, unknown>>\n readonly userId: Option.Option<string>\n readonly outputConfig: Option.Option<Record<string, unknown>>\n readonly cacheControl: Option.Option<CacheControl>\n}): Result.Result<RequestBody, JsonParseError> =>\n pipe(\n groupedMessages(params.history),\n Result.map((messages): RequestBody => ({\n model: params.model,\n messages,\n max_tokens: params.maxTokens,\n ...Option.match(systemFromHistory(params.history), {\n onNone: () => ({}),\n onSome: (system) => ({ system }),\n }),\n ...Option.match(params.temperature, {\n onNone: () => ({}),\n onSome: (temperature) => ({ temperature }),\n }),\n ...Option.match(params.topP, {\n onNone: () => ({}),\n onSome: (top_p) => ({ top_p }),\n }),\n ...Option.match(params.topK, {\n onNone: () => ({}),\n onSome: (top_k) => ({ top_k }),\n }),\n ...Option.match(params.stopSequences, {\n onNone: () => ({}),\n onSome: (stop_sequences) => ({ stop_sequences }),\n }),\n ...Option.match(params.thinking, {\n onNone: () => ({}),\n onSome: (thinking) => ({ thinking }),\n }),\n ...Option.match(params.tools, {\n onNone: () => ({}),\n onSome: (tools) => ({ tools }),\n }),\n ...Option.match(params.toolChoice, {\n onNone: () => ({}),\n onSome: (tool_choice) => ({ tool_choice }),\n }),\n ...Option.match(params.userId, {\n onNone: () => ({}),\n onSome: (user_id) => ({ metadata: { user_id } }),\n }),\n ...Option.match(params.outputConfig, {\n onNone: () => ({}),\n onSome: (output_config) => ({ output_config }),\n }),\n ...Option.match(params.cacheControl, {\n onNone: () => ({}),\n onSome: (cache_control) => ({ cache_control }),\n }),\n stream: true,\n })),\n )\n\n// ---------------------------------------------------------------------------\n// Stream-level state - assemble content blocks index-by-index, then emit\n// our `Items.HistoryItem[]` when `message_stop` lands.\n// ---------------------------------------------------------------------------\n\ntype BlockBuffer = {\n readonly type: WireContentBlock[\"type\"]\n readonly text: string\n readonly inputJson: string\n readonly thinking: string\n readonly signature: string\n readonly id: Option.Option<string>\n readonly name: Option.Option<string>\n readonly redactedData: Option.Option<string>\n}\n\nconst emptyBlock = (type: WireContentBlock[\"type\"]): BlockBuffer => ({\n type,\n text: \"\",\n inputJson: \"\",\n thinking: \"\",\n signature: \"\",\n id: Option.none(),\n name: Option.none(),\n redactedData: Option.none(),\n})\n\nexport type Accumulator = {\n readonly blocks: Readonly<Record<number, BlockBuffer>>\n readonly stopReason: Option.Option<string>\n readonly usage: Items.Usage\n}\n\nexport const emptyAccumulator: Accumulator = {\n blocks: {},\n stopReason: Option.none(),\n usage: {},\n}\n\nconst replaceBlock = (acc: Accumulator, index: number, block: BlockBuffer): Accumulator => ({\n ...acc,\n blocks: { ...acc.blocks, [index]: block },\n})\n\nconst updateBlock = (\n acc: Accumulator,\n index: number,\n patch: (block: BlockBuffer) => BlockBuffer,\n): Accumulator => replaceBlock(acc, index, patch(acc.blocks[index] ?? emptyBlock(\"text\")))\n\nexport const startBlock = (acc: Accumulator, index: number, block: WireContentBlock): Accumulator =>\n Match.value(block).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n text: () => replaceBlock(acc, index, emptyBlock(\"text\")),\n tool_use: (b) =>\n replaceBlock(acc, index, {\n ...emptyBlock(\"tool_use\"),\n id: Option.some(b.id),\n name: Option.some(b.name),\n inputJson: typeof b.input === \"string\" ? b.input : \"\",\n }),\n thinking: () => replaceBlock(acc, index, emptyBlock(\"thinking\")),\n redacted_thinking: (b) =>\n replaceBlock(acc, index, {\n ...emptyBlock(\"redacted_thinking\"),\n redactedData: Option.some(b.data),\n }),\n }),\n )\n\nexport const appendTextDelta = (acc: Accumulator, index: number, text: string): Accumulator =>\n updateBlock(acc, index, (b) => ({ ...b, text: b.text + text }))\n\nexport const appendInputJsonDelta = (\n acc: Accumulator,\n index: number,\n partial: string,\n): Accumulator => updateBlock(acc, index, (b) => ({ ...b, inputJson: b.inputJson + partial }))\n\nexport const appendThinkingDelta = (\n acc: Accumulator,\n index: number,\n thinking: string,\n): Accumulator => updateBlock(acc, index, (b) => ({ ...b, thinking: b.thinking + thinking }))\n\nexport const appendSignatureDelta = (\n acc: Accumulator,\n index: number,\n signature: string,\n): Accumulator => updateBlock(acc, index, (b) => ({ ...b, signature: b.signature + signature }))\n\nexport const setStopReason = (acc: Accumulator, reason: string): Accumulator => ({\n ...acc,\n stopReason: Option.some(reason),\n})\n\n// Anthropic splits input across three billed buckets: `input_tokens`\n// (post-breakpoint), cache reads, and cache writes. All three are kept;\n// `input_tokens` is not the input total. Each field carries over from the\n// prior value since a streamed turn spreads usage across events (input +\n// cache on message_start, output_tokens on message_delta), so total_tokens is\n// summed from the accumulated buckets, not one event.\nexport const mergeUsage = (acc: Accumulator, wire: WireUsage): Accumulator => {\n const prev = acc.usage\n const input_tokens = wire.input_tokens ?? prev.input_tokens\n const output_tokens = wire.output_tokens ?? prev.output_tokens\n const cached_tokens = wire.cache_read_input_tokens ?? prev.input_tokens_details?.cached_tokens\n const cache_write_tokens =\n wire.cache_creation_input_tokens ?? prev.input_tokens_details?.cache_write_tokens\n\n const buckets = [input_tokens, cached_tokens, cache_write_tokens, output_tokens]\n const total_tokens = buckets.every((v) => v === undefined)\n ? undefined\n : buckets.reduce<number>((sum, v) => sum + (v ?? 0), 0)\n\n const details: Items.InputTokensDetails = {\n ...(cached_tokens !== undefined && { cached_tokens }),\n ...(cache_write_tokens !== undefined && { cache_write_tokens }),\n }\n const usage: Items.Usage = {\n ...(input_tokens !== undefined && { input_tokens }),\n ...(output_tokens !== undefined && { output_tokens }),\n ...(total_tokens !== undefined && { total_tokens }),\n ...(Object.keys(details).length > 0 && { input_tokens_details: details }),\n }\n return { ...acc, usage }\n}\n\nconst stopReasonFromAnthropic = (reason: Option.Option<string>): Turn[\"stop_reason\"] =>\n Option.match(reason, {\n onNone: () => \"stop\" as const,\n onSome: (r) =>\n Match.value(r).pipe(\n Match.when(\"tool_use\", () => \"tool_calls\" as const),\n Match.when(\"max_tokens\", () => \"max_tokens\" as const),\n Match.orElse(() => \"stop\" as const),\n ),\n })\n\nconst blocksByIndex = (acc: Accumulator): ReadonlyArray<BlockBuffer> =>\n pipe(\n Object.keys(acc.blocks),\n Arr.map((k) => Number(k)),\n Arr.sort(Order.Number),\n Arr.map((i) => acc.blocks[i]!),\n )\n\nconst blockToItems = (block: BlockBuffer): ReadonlyArray<Items.HistoryItem> =>\n Match.value(block.type).pipe(\n Match.when(\"text\", (): ReadonlyArray<Items.HistoryItem> =>\n block.text.length === 0\n ? []\n : [\n {\n type: \"message\",\n role: \"assistant\",\n content: [{ type: \"output_text\", text: block.text }],\n },\n ],\n ),\n Match.when(\"tool_use\", (): ReadonlyArray<Items.HistoryItem> => [\n {\n type: \"function_call\",\n call_id: Option.getOrElse(block.id, () => \"\"),\n name: Option.getOrElse(block.name, () => \"\"),\n arguments: block.inputJson,\n },\n ]),\n Match.when(\"thinking\", (): ReadonlyArray<Items.HistoryItem> => [\n {\n type: \"reasoning\",\n ...(block.thinking.length > 0 && { summary: block.thinking }),\n ...(block.signature.length > 0 && { signature: block.signature }),\n },\n ]),\n Match.when(\"redacted_thinking\", (): ReadonlyArray<Items.HistoryItem> => [\n {\n type: \"reasoning\",\n ...Option.match(block.redactedData, {\n onNone: () => ({}),\n onSome: (signature) => ({ signature }),\n }),\n },\n ]),\n Match.exhaustive,\n )\n\ntype MergeAcc = {\n readonly out: ReadonlyArray<Items.HistoryItem>\n}\n\nconst mergeStep = (acc: MergeAcc, item: Items.HistoryItem): MergeAcc => {\n const last = Arr.last(acc.out)\n if (\n Option.isSome(last) &&\n last.value.type === \"message\" &&\n last.value.role === \"assistant\" &&\n item.type === \"message\" &&\n item.role === \"assistant\"\n ) {\n const merged: Items.Message = {\n ...last.value,\n content: [...last.value.content, ...item.content],\n }\n return { out: [...acc.out.slice(0, -1), merged] }\n }\n return { out: [...acc.out, item] }\n}\n\nconst mergeAdjacentAssistantText = (\n items: ReadonlyArray<Items.HistoryItem>,\n): ReadonlyArray<Items.HistoryItem> => Arr.reduce(items, { out: [] } as MergeAcc, mergeStep).out\n\nexport const accumulatorToTurn = (acc: Accumulator): Turn => ({\n items: pipe(blocksByIndex(acc), Arr.flatMap(blockToItems), mergeAdjacentAssistantText),\n usage: acc.usage,\n stop_reason: stopReasonFromAnthropic(acc.stopReason),\n})\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"codec.mjs","names":["Arr"],"sources":["../src/codec.ts"],"sourcesContent":["import { Array as Arr, Match, Option, Order, Result, Schema, pipe } from \"effect\"\nimport { Base64 } from \"effect/encoding\"\nimport * as Items from \"@effect-uai/core/Items\"\nimport { JsonParseError } from \"@effect-uai/core/JSONL\"\nimport * as ToolOutput from \"@effect-uai/core/ToolOutput\"\nimport type { Turn } from \"@effect-uai/core/Turn\"\n\n// ---------------------------------------------------------------------------\n// Wire schemas - subset of Anthropic Messages API we consume.\n// Reference: https://platform.claude.com/docs/en/api/messages\n// ---------------------------------------------------------------------------\n\nconst WireTextBlock = Schema.Struct({\n type: Schema.Literal(\"text\"),\n text: Schema.String,\n})\n\nconst WireToolUseBlock = Schema.Struct({\n type: Schema.Literal(\"tool_use\"),\n id: Schema.String,\n name: Schema.String,\n input: Schema.Unknown,\n})\n\nconst WireThinkingBlock = Schema.Struct({\n type: Schema.Literal(\"thinking\"),\n thinking: Schema.String,\n signature: Schema.optional(Schema.String),\n})\n\nconst WireRedactedThinkingBlock = Schema.Struct({\n type: Schema.Literal(\"redacted_thinking\"),\n data: Schema.String,\n})\n\nexport const WireContentBlock = Schema.Union([\n WireTextBlock,\n WireToolUseBlock,\n WireThinkingBlock,\n WireRedactedThinkingBlock,\n])\nexport type WireContentBlock = typeof WireContentBlock.Type\n\nconst WireUsage = Schema.Struct({\n input_tokens: Schema.optional(Schema.Number),\n output_tokens: Schema.optional(Schema.Number),\n cache_creation_input_tokens: Schema.optional(Schema.NullOr(Schema.Number)),\n cache_read_input_tokens: Schema.optional(Schema.NullOr(Schema.Number)),\n})\nexport type WireUsage = typeof WireUsage.Type\n\n// ---------------------------------------------------------------------------\n// History → request body\n// ---------------------------------------------------------------------------\n\ntype RequestTextContent = {\n readonly type: \"text\"\n readonly text: string\n}\n\ntype RequestToolResultContent = {\n readonly type: \"tool_result\"\n readonly tool_use_id: string\n readonly content: string | ReadonlyArray<RequestToolResultBlock>\n readonly is_error?: boolean\n}\n\ntype RequestDocumentContent = {\n readonly type: \"document\"\n readonly source:\n | { readonly type: \"url\"; readonly url: string }\n | { readonly type: \"base64\"; readonly media_type: string; readonly data: string }\n readonly title?: string\n}\n\ntype RequestToolResultBlock = RequestTextContent | RequestImageContent | RequestDocumentContent\n\ntype RequestToolUseContent = {\n readonly type: \"tool_use\"\n readonly id: string\n readonly name: string\n readonly input: unknown\n}\n\ntype RequestThinkingContent = {\n readonly type: \"thinking\"\n readonly thinking: string\n readonly signature?: string\n}\n\ntype RequestRedactedThinkingContent = {\n readonly type: \"redacted_thinking\"\n readonly data: string\n}\n\ntype RequestImageContent = {\n readonly type: \"image\"\n readonly source:\n | { readonly type: \"url\"; readonly url: string }\n | { readonly type: \"base64\"; readonly media_type: string; readonly data: string }\n}\n\ntype RequestUserContentBlock = RequestTextContent | RequestToolResultContent | RequestImageContent\n\ntype RequestAssistantContentBlock =\n | RequestTextContent\n | RequestToolUseContent\n | RequestThinkingContent\n | RequestRedactedThinkingContent\n\ntype RequestUserMessage = {\n readonly role: \"user\"\n readonly content: ReadonlyArray<RequestUserContentBlock>\n}\n\ntype RequestAssistantMessage = {\n readonly role: \"assistant\"\n readonly content: ReadonlyArray<RequestAssistantContentBlock>\n}\n\ntype RequestMessage = RequestUserMessage | RequestAssistantMessage\n\nconst blockText = Match.type<Items.ContentBlock>().pipe(\n Match.discriminatorsExhaustive(\"type\")({\n input_text: (b) => b.text,\n input_image: () => \"\",\n output_text: (b) => b.text,\n output_image: () => \"\",\n refusal: (b) => b.text,\n }),\n)\n\nconst messageText = (message: Items.Message): string => message.content.map(blockText).join(\"\")\n\nconst imageSourceToWire = Match.type<Items.InputImage[\"source\"]>().pipe(\n Match.tag(\"url\", (s): RequestImageContent[\"source\"] => ({ type: \"url\", url: s.url })),\n Match.tag(\"base64\", (s): RequestImageContent[\"source\"] => ({\n type: \"base64\",\n media_type: s.mimeType,\n data: s.base64,\n })),\n Match.tag(\"bytes\", (s): RequestImageContent[\"source\"] => ({\n type: \"base64\",\n media_type: s.mimeType,\n data: Base64.encode(s.bytes),\n })),\n Match.exhaustive,\n)\n\nconst fileSourceToWire = Match.type<Items.InputFile[\"source\"]>().pipe(\n Match.tag(\"url\", (s): RequestDocumentContent[\"source\"] => ({ type: \"url\", url: s.url })),\n Match.tag(\"base64\", (s): RequestDocumentContent[\"source\"] => ({\n type: \"base64\",\n media_type: s.mimeType,\n data: s.base64,\n })),\n Match.tag(\"bytes\", (s): RequestDocumentContent[\"source\"] => ({\n type: \"base64\",\n media_type: s.mimeType,\n data: Base64.encode(s.bytes),\n })),\n Match.exhaustive,\n)\n\nconst toolResultBlock = Match.type<Items.ToolOutputContent>().pipe(\n Match.discriminatorsExhaustive(\"type\")({\n input_text: (p): RequestToolResultBlock => ({ type: \"text\", text: ToolOutput.partText(p) }),\n input_json: (p): RequestToolResultBlock => ({ type: \"text\", text: ToolOutput.partText(p) }),\n input_image: (p): RequestToolResultBlock => ({\n type: \"image\",\n source: imageSourceToWire(p.source),\n }),\n input_file: (p): RequestToolResultBlock => ({\n type: \"document\",\n source: fileSourceToWire(p.source),\n ...(p.filename !== undefined && { title: p.filename }),\n }),\n }),\n)\n\n// Text and JSON only: one string. With media: image and document blocks.\nconst toolResult = (o: Items.ToolCallOutput): RequestToolResultContent => ({\n type: \"tool_result\",\n tool_use_id: o.call_id,\n content: Option.getOrElse(ToolOutput.text(o.output), () => o.output.map(toolResultBlock)),\n ...(o.isError === true && { is_error: true }),\n})\n\nconst userContentBlock = (\n block: Items.ContentBlock,\n): Result.Result<RequestUserContentBlock, void> =>\n Match.value(block).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n input_text: (b) =>\n b.text.length === 0\n ? Result.failVoid\n : Result.succeed({ type: \"text\" as const, text: b.text }),\n input_image: (b) =>\n Result.succeed({ type: \"image\" as const, source: imageSourceToWire(b.source) }),\n // Assistant content; never appears on a user message in practice. Skip.\n output_text: () => Result.failVoid,\n // No assistant-image block here either. Dropped rather than relocated\n // onto a user message, which is the caller's call: `Turn.imagesAsInput`.\n output_image: () => Result.failVoid,\n refusal: () => Result.failVoid,\n }),\n )\n\nconst parseJson = (s: string): Result.Result<unknown, JsonParseError> =>\n Result.try({\n try: () => JSON.parse(s) as unknown,\n catch: (cause) => new JsonParseError({ line: s, cause }),\n })\n\ntype RoleBucket = \"user\" | \"assistant\" | \"system\"\n\nconst roleBucket = (item: Items.HistoryItem): RoleBucket =>\n Match.value(item).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n message: (m) => m.role,\n function_call: () => \"assistant\" as const,\n function_call_output: () => \"user\" as const,\n reasoning: () => \"assistant\" as const,\n }),\n )\n\nconst itemToUserBlocks = (item: Items.HistoryItem): ReadonlyArray<RequestUserContentBlock> =>\n Match.value(item).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n message: (m): ReadonlyArray<RequestUserContentBlock> =>\n m.role === \"user\" ? pipe(m.content, Arr.filterMap(userContentBlock)) : [],\n function_call: (): ReadonlyArray<RequestUserContentBlock> => [],\n function_call_output: (o): ReadonlyArray<RequestUserContentBlock> => [toolResult(o)],\n reasoning: (): ReadonlyArray<RequestUserContentBlock> => [],\n }),\n )\n\nconst itemToAssistantBlocks = (\n item: Items.HistoryItem,\n): Result.Result<ReadonlyArray<RequestAssistantContentBlock>, JsonParseError> =>\n Match.value(item).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n message: (m): Result.Result<ReadonlyArray<RequestAssistantContentBlock>, JsonParseError> => {\n const text = messageText(m)\n return Result.succeed(\n m.role === \"assistant\" && text.length > 0 ? [{ type: \"text\", text }] : [],\n )\n },\n function_call: (f) =>\n pipe(\n parseJson(f.arguments),\n Result.map((input): ReadonlyArray<RequestAssistantContentBlock> => [\n { type: \"tool_use\", id: f.call_id, name: f.name, input },\n ]),\n ),\n function_call_output: (): Result.Result<\n ReadonlyArray<RequestAssistantContentBlock>,\n JsonParseError\n > => Result.succeed([]),\n reasoning: (r) => {\n const blocks: ReadonlyArray<RequestAssistantContentBlock> =\n r.summary !== undefined\n ? [\n {\n type: \"thinking\",\n thinking: r.summary,\n ...(r.signature !== undefined && { signature: r.signature }),\n },\n ]\n : r.signature !== undefined\n ? [{ type: \"redacted_thinking\", data: r.signature }]\n : []\n return Result.succeed(blocks)\n },\n }),\n )\n\ntype GroupAcc = {\n readonly messages: ReadonlyArray<RequestMessage>\n readonly currentRole: Option.Option<\"user\" | \"assistant\">\n readonly userBuf: ReadonlyArray<RequestUserContentBlock>\n readonly assistantBuf: ReadonlyArray<RequestAssistantContentBlock>\n}\n\nconst flushAcc = (acc: GroupAcc): ReadonlyArray<RequestMessage> =>\n Option.match(acc.currentRole, {\n onNone: () => acc.messages,\n onSome: (role) =>\n role === \"user\" && acc.userBuf.length > 0\n ? [...acc.messages, { role: \"user\", content: acc.userBuf }]\n : role === \"assistant\" && acc.assistantBuf.length > 0\n ? [...acc.messages, { role: \"assistant\", content: acc.assistantBuf }]\n : acc.messages,\n })\n\nconst appendUser = (acc: GroupAcc, blocks: ReadonlyArray<RequestUserContentBlock>): GroupAcc =>\n blocks.length === 0\n ? acc\n : Option.isSome(acc.currentRole) && acc.currentRole.value === \"user\"\n ? { ...acc, userBuf: [...acc.userBuf, ...blocks] }\n : {\n messages: flushAcc(acc),\n currentRole: Option.some(\"user\"),\n userBuf: blocks,\n assistantBuf: [],\n }\n\nconst appendAssistant = (\n acc: GroupAcc,\n blocks: ReadonlyArray<RequestAssistantContentBlock>,\n): GroupAcc =>\n blocks.length === 0\n ? acc\n : Option.isSome(acc.currentRole) && acc.currentRole.value === \"assistant\"\n ? { ...acc, assistantBuf: [...acc.assistantBuf, ...blocks] }\n : {\n messages: flushAcc(acc),\n currentRole: Option.some(\"assistant\"),\n userBuf: [],\n assistantBuf: blocks,\n }\n\nconst groupStep = (\n acc: GroupAcc,\n item: Items.HistoryItem,\n): Result.Result<GroupAcc, JsonParseError> => {\n const bucket = roleBucket(item)\n if (bucket === \"system\") return Result.succeed(acc)\n if (bucket === \"user\") {\n return Result.succeed(appendUser(acc, itemToUserBlocks(item)))\n }\n return pipe(\n itemToAssistantBlocks(item),\n Result.map((blocks) => appendAssistant(acc, blocks)),\n )\n}\n\n/**\n * Group consecutive same-role items into Anthropic-shaped messages.\n * Anthropic requires strict user/assistant alternation; consecutive items\n * from the same role are folded into one message's `content`. Fails if any\n * `function_call.arguments` is not valid JSON, since Anthropic's wire shape\n * requires an object input.\n */\nconst groupedMessages = (\n history: ReadonlyArray<Items.HistoryItem>,\n): Result.Result<ReadonlyArray<RequestMessage>, JsonParseError> => {\n const initial: Result.Result<GroupAcc, JsonParseError> = Result.succeed({\n messages: [],\n currentRole: Option.none(),\n userBuf: [],\n assistantBuf: [],\n })\n return pipe(\n Arr.reduce(history, initial, (acc, item) => Result.flatMap(acc, (a) => groupStep(a, item))),\n Result.map(flushAcc),\n )\n}\n\nconst isSystemMessage = (item: Items.HistoryItem): item is Items.Message =>\n item.type === \"message\" && item.role === \"system\"\n\nconst systemFromHistory = (history: ReadonlyArray<Items.HistoryItem>): Option.Option<string> => {\n const texts = pipe(\n history,\n Arr.filterMap((item) =>\n isSystemMessage(item) ? Result.succeed(messageText(item)) : Result.failVoid,\n ),\n Arr.filter((s) => s.length > 0),\n )\n return texts.length === 0 ? Option.none() : Option.some(texts.join(\"\\n\"))\n}\n\nexport type ThinkingConfig = {\n readonly type: \"enabled\"\n readonly budget_tokens: number\n}\n\n/** Cache lifetime. `1h` writes cost 2x base input; `5m` costs 1.25x. */\nexport type CacheTtl = \"5m\" | \"1h\"\n\n/**\n * Request-level cache breakpoint. Anthropic applies it to the last cacheable\n * block and moves it forward as the conversation grows, so the cached prefix\n * (`tools` → `system` → `messages`) follows the conversation without the\n * caller re-placing anything.\n */\nexport type CacheControl = {\n readonly type: \"ephemeral\"\n readonly ttl?: CacheTtl\n}\n\nexport type RequestBody = {\n readonly model: string\n readonly messages: ReadonlyArray<RequestMessage>\n readonly max_tokens: number\n readonly system?: string\n readonly temperature?: number\n readonly top_p?: number\n readonly top_k?: number\n readonly stop_sequences?: ReadonlyArray<string>\n readonly thinking?: ThinkingConfig\n readonly tools?: ReadonlyArray<Record<string, unknown>>\n readonly tool_choice?: Record<string, unknown>\n readonly metadata?: { readonly user_id: string }\n readonly output_config?: Record<string, unknown>\n readonly cache_control?: CacheControl\n readonly stream: true\n}\n\nexport const buildRequestBody = (params: {\n readonly model: string\n readonly history: ReadonlyArray<Items.HistoryItem>\n readonly maxTokens: number\n readonly temperature: Option.Option<number>\n readonly topP: Option.Option<number>\n readonly topK: Option.Option<number>\n readonly stopSequences: Option.Option<ReadonlyArray<string>>\n readonly thinking: Option.Option<ThinkingConfig>\n readonly tools: Option.Option<ReadonlyArray<Record<string, unknown>>>\n readonly toolChoice: Option.Option<Record<string, unknown>>\n readonly userId: Option.Option<string>\n readonly outputConfig: Option.Option<Record<string, unknown>>\n readonly cacheControl: Option.Option<CacheControl>\n}): Result.Result<RequestBody, JsonParseError> =>\n pipe(\n groupedMessages(params.history),\n Result.map((messages): RequestBody => ({\n model: params.model,\n messages,\n max_tokens: params.maxTokens,\n ...Option.match(systemFromHistory(params.history), {\n onNone: () => ({}),\n onSome: (system) => ({ system }),\n }),\n ...Option.match(params.temperature, {\n onNone: () => ({}),\n onSome: (temperature) => ({ temperature }),\n }),\n ...Option.match(params.topP, {\n onNone: () => ({}),\n onSome: (top_p) => ({ top_p }),\n }),\n ...Option.match(params.topK, {\n onNone: () => ({}),\n onSome: (top_k) => ({ top_k }),\n }),\n ...Option.match(params.stopSequences, {\n onNone: () => ({}),\n onSome: (stop_sequences) => ({ stop_sequences }),\n }),\n ...Option.match(params.thinking, {\n onNone: () => ({}),\n onSome: (thinking) => ({ thinking }),\n }),\n ...Option.match(params.tools, {\n onNone: () => ({}),\n onSome: (tools) => ({ tools }),\n }),\n ...Option.match(params.toolChoice, {\n onNone: () => ({}),\n onSome: (tool_choice) => ({ tool_choice }),\n }),\n ...Option.match(params.userId, {\n onNone: () => ({}),\n onSome: (user_id) => ({ metadata: { user_id } }),\n }),\n ...Option.match(params.outputConfig, {\n onNone: () => ({}),\n onSome: (output_config) => ({ output_config }),\n }),\n ...Option.match(params.cacheControl, {\n onNone: () => ({}),\n onSome: (cache_control) => ({ cache_control }),\n }),\n stream: true,\n })),\n )\n\n// ---------------------------------------------------------------------------\n// Stream-level state - assemble content blocks index-by-index, then emit\n// our `Items.HistoryItem[]` when `message_stop` lands.\n// ---------------------------------------------------------------------------\n\ntype BlockBuffer = {\n readonly type: WireContentBlock[\"type\"]\n readonly text: string\n readonly inputJson: string\n readonly thinking: string\n readonly signature: string\n readonly id: Option.Option<string>\n readonly name: Option.Option<string>\n readonly redactedData: Option.Option<string>\n}\n\nconst emptyBlock = (type: WireContentBlock[\"type\"]): BlockBuffer => ({\n type,\n text: \"\",\n inputJson: \"\",\n thinking: \"\",\n signature: \"\",\n id: Option.none(),\n name: Option.none(),\n redactedData: Option.none(),\n})\n\nexport type Accumulator = {\n readonly blocks: Readonly<Record<number, BlockBuffer>>\n readonly stopReason: Option.Option<string>\n readonly usage: Items.Usage\n}\n\nexport const emptyAccumulator: Accumulator = {\n blocks: {},\n stopReason: Option.none(),\n usage: {},\n}\n\nconst replaceBlock = (acc: Accumulator, index: number, block: BlockBuffer): Accumulator => ({\n ...acc,\n blocks: { ...acc.blocks, [index]: block },\n})\n\nconst updateBlock = (\n acc: Accumulator,\n index: number,\n patch: (block: BlockBuffer) => BlockBuffer,\n): Accumulator => replaceBlock(acc, index, patch(acc.blocks[index] ?? emptyBlock(\"text\")))\n\nexport const startBlock = (acc: Accumulator, index: number, block: WireContentBlock): Accumulator =>\n Match.value(block).pipe(\n Match.discriminatorsExhaustive(\"type\")({\n text: () => replaceBlock(acc, index, emptyBlock(\"text\")),\n tool_use: (b) =>\n replaceBlock(acc, index, {\n ...emptyBlock(\"tool_use\"),\n id: Option.some(b.id),\n name: Option.some(b.name),\n inputJson: typeof b.input === \"string\" ? b.input : \"\",\n }),\n thinking: () => replaceBlock(acc, index, emptyBlock(\"thinking\")),\n redacted_thinking: (b) =>\n replaceBlock(acc, index, {\n ...emptyBlock(\"redacted_thinking\"),\n redactedData: Option.some(b.data),\n }),\n }),\n )\n\nexport const appendTextDelta = (acc: Accumulator, index: number, text: string): Accumulator =>\n updateBlock(acc, index, (b) => ({ ...b, text: b.text + text }))\n\nexport const appendInputJsonDelta = (\n acc: Accumulator,\n index: number,\n partial: string,\n): Accumulator => updateBlock(acc, index, (b) => ({ ...b, inputJson: b.inputJson + partial }))\n\nexport const appendThinkingDelta = (\n acc: Accumulator,\n index: number,\n thinking: string,\n): Accumulator => updateBlock(acc, index, (b) => ({ ...b, thinking: b.thinking + thinking }))\n\nexport const appendSignatureDelta = (\n acc: Accumulator,\n index: number,\n signature: string,\n): Accumulator => updateBlock(acc, index, (b) => ({ ...b, signature: b.signature + signature }))\n\nexport const setStopReason = (acc: Accumulator, reason: string): Accumulator => ({\n ...acc,\n stopReason: Option.some(reason),\n})\n\n// Anthropic splits input across three billed buckets: `input_tokens`\n// (post-breakpoint), cache reads, and cache writes. All three are kept;\n// `input_tokens` is not the input total. Each field carries over from the\n// prior value since a streamed turn spreads usage across events (input +\n// cache on message_start, output_tokens on message_delta), so total_tokens is\n// summed from the accumulated buckets, not one event.\nexport const mergeUsage = (acc: Accumulator, wire: WireUsage): Accumulator => {\n const prev = acc.usage\n const input_tokens = wire.input_tokens ?? prev.input_tokens\n const output_tokens = wire.output_tokens ?? prev.output_tokens\n const cached_tokens = wire.cache_read_input_tokens ?? prev.input_tokens_details?.cached_tokens\n const cache_write_tokens =\n wire.cache_creation_input_tokens ?? prev.input_tokens_details?.cache_write_tokens\n\n const buckets = [input_tokens, cached_tokens, cache_write_tokens, output_tokens]\n const total_tokens = buckets.every((v) => v === undefined)\n ? undefined\n : buckets.reduce<number>((sum, v) => sum + (v ?? 0), 0)\n\n const details: Items.InputTokensDetails = {\n ...(cached_tokens !== undefined && { cached_tokens }),\n ...(cache_write_tokens !== undefined && { cache_write_tokens }),\n }\n const usage: Items.Usage = {\n ...(input_tokens !== undefined && { input_tokens }),\n ...(output_tokens !== undefined && { output_tokens }),\n ...(total_tokens !== undefined && { total_tokens }),\n ...(Object.keys(details).length > 0 && { input_tokens_details: details }),\n }\n return { ...acc, usage }\n}\n\nconst stopReasonFromAnthropic = (reason: Option.Option<string>): Turn[\"stop_reason\"] =>\n Option.match(reason, {\n onNone: () => \"stop\" as const,\n onSome: (r) =>\n Match.value(r).pipe(\n Match.when(\"tool_use\", () => \"tool_calls\" as const),\n Match.when(\"max_tokens\", () => \"max_tokens\" as const),\n Match.orElse(() => \"stop\" as const),\n ),\n })\n\nconst blocksByIndex = (acc: Accumulator): ReadonlyArray<BlockBuffer> =>\n pipe(\n Object.keys(acc.blocks),\n Arr.map((k) => Number(k)),\n Arr.sort(Order.Number),\n Arr.map((i) => acc.blocks[i]!),\n )\n\nconst blockToItems = (block: BlockBuffer): ReadonlyArray<Items.HistoryItem> =>\n Match.value(block.type).pipe(\n Match.when(\"text\", (): ReadonlyArray<Items.HistoryItem> =>\n block.text.length === 0\n ? []\n : [\n {\n type: \"message\",\n role: \"assistant\",\n content: [{ type: \"output_text\", text: block.text }],\n },\n ],\n ),\n Match.when(\"tool_use\", (): ReadonlyArray<Items.HistoryItem> => [\n {\n type: \"function_call\",\n call_id: Option.getOrElse(block.id, () => \"\"),\n name: Option.getOrElse(block.name, () => \"\"),\n arguments: block.inputJson,\n },\n ]),\n Match.when(\"thinking\", (): ReadonlyArray<Items.HistoryItem> => [\n {\n type: \"reasoning\",\n ...(block.thinking.length > 0 && { summary: block.thinking }),\n ...(block.signature.length > 0 && { signature: block.signature }),\n },\n ]),\n Match.when(\"redacted_thinking\", (): ReadonlyArray<Items.HistoryItem> => [\n {\n type: \"reasoning\",\n ...Option.match(block.redactedData, {\n onNone: () => ({}),\n onSome: (signature) => ({ signature }),\n }),\n },\n ]),\n Match.exhaustive,\n )\n\ntype MergeAcc = {\n readonly out: ReadonlyArray<Items.HistoryItem>\n}\n\nconst mergeStep = (acc: MergeAcc, item: Items.HistoryItem): MergeAcc => {\n const last = Arr.last(acc.out)\n if (\n Option.isSome(last) &&\n last.value.type === \"message\" &&\n last.value.role === \"assistant\" &&\n item.type === \"message\" &&\n item.role === \"assistant\"\n ) {\n const merged: Items.Message = {\n ...last.value,\n content: [...last.value.content, ...item.content],\n }\n return { out: [...acc.out.slice(0, -1), merged] }\n }\n return { out: [...acc.out, item] }\n}\n\nconst mergeAdjacentAssistantText = (\n items: ReadonlyArray<Items.HistoryItem>,\n): ReadonlyArray<Items.HistoryItem> => Arr.reduce(items, { out: [] } as MergeAcc, mergeStep).out\n\nexport const accumulatorToTurn = (acc: Accumulator): Turn => ({\n items: pipe(blocksByIndex(acc), Arr.flatMap(blockToItems), mergeAdjacentAssistantText),\n usage: acc.usage,\n stop_reason: stopReasonFromAnthropic(acc.stopReason),\n})\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { buildRequestBody } from "./codec.mjs";
|
|
2
|
+
import { n as describe, r as it, t as globalExpect } from "./index.C4tKZ0yW-xEu55bU4.mjs";
|
|
3
|
+
import { Option, Result } from "effect";
|
|
4
|
+
import { fileBase64, fileUrl } from "@effect-uai/core/File";
|
|
5
|
+
import { imageBytes } from "@effect-uai/core/Image";
|
|
6
|
+
import * as Items from "@effect-uai/core/Items";
|
|
7
|
+
//#region src/codec.test.ts
|
|
8
|
+
const toolResultFor = (output) => {
|
|
9
|
+
return Result.getOrThrow(buildRequestBody({
|
|
10
|
+
model: "claude-sonnet-4-5",
|
|
11
|
+
history: [
|
|
12
|
+
Items.userText("go"),
|
|
13
|
+
{
|
|
14
|
+
type: "function_call",
|
|
15
|
+
call_id: "c1",
|
|
16
|
+
name: "shot",
|
|
17
|
+
arguments: "{}"
|
|
18
|
+
},
|
|
19
|
+
output
|
|
20
|
+
],
|
|
21
|
+
maxTokens: 1024,
|
|
22
|
+
temperature: Option.none(),
|
|
23
|
+
topP: Option.none(),
|
|
24
|
+
topK: Option.none(),
|
|
25
|
+
stopSequences: Option.none(),
|
|
26
|
+
thinking: Option.none(),
|
|
27
|
+
tools: Option.none(),
|
|
28
|
+
toolChoice: Option.none(),
|
|
29
|
+
userId: Option.none(),
|
|
30
|
+
outputConfig: Option.none(),
|
|
31
|
+
cacheControl: Option.none()
|
|
32
|
+
})).messages.at(-1)?.content;
|
|
33
|
+
};
|
|
34
|
+
const outputOf = (output, isError) => toolResultFor({
|
|
35
|
+
type: "function_call_output",
|
|
36
|
+
call_id: "c1",
|
|
37
|
+
output,
|
|
38
|
+
...isError !== void 0 && { isError }
|
|
39
|
+
});
|
|
40
|
+
describe("tool_result", () => {
|
|
41
|
+
it("sends text and JSON as one string and flags failures with is_error", () => {
|
|
42
|
+
globalExpect(outputOf([Items.json({
|
|
43
|
+
_tag: "CityNotFound",
|
|
44
|
+
city: "Atlantis"
|
|
45
|
+
})], true)).toEqual([{
|
|
46
|
+
type: "tool_result",
|
|
47
|
+
tool_use_id: "c1",
|
|
48
|
+
content: "{\"_tag\":\"CityNotFound\",\"city\":\"Atlantis\"}",
|
|
49
|
+
is_error: true
|
|
50
|
+
}]);
|
|
51
|
+
});
|
|
52
|
+
it("sends media as image and document blocks, the filename as the document title", () => {
|
|
53
|
+
globalExpect(outputOf([
|
|
54
|
+
Items.text("Home"),
|
|
55
|
+
Items.image(imageBytes(new Uint8Array([
|
|
56
|
+
1,
|
|
57
|
+
2,
|
|
58
|
+
3
|
|
59
|
+
]), "image/png")),
|
|
60
|
+
Items.file(fileBase64("JVBE", "application/pdf"), "report.pdf"),
|
|
61
|
+
Items.file(fileUrl("https://x/a.pdf"))
|
|
62
|
+
])).toEqual([{
|
|
63
|
+
type: "tool_result",
|
|
64
|
+
tool_use_id: "c1",
|
|
65
|
+
content: [
|
|
66
|
+
{
|
|
67
|
+
type: "text",
|
|
68
|
+
text: "Home"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
type: "image",
|
|
72
|
+
source: {
|
|
73
|
+
type: "base64",
|
|
74
|
+
media_type: "image/png",
|
|
75
|
+
data: "AQID"
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
type: "document",
|
|
80
|
+
source: {
|
|
81
|
+
type: "base64",
|
|
82
|
+
media_type: "application/pdf",
|
|
83
|
+
data: "JVBE"
|
|
84
|
+
},
|
|
85
|
+
title: "report.pdf"
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
type: "document",
|
|
89
|
+
source: {
|
|
90
|
+
type: "url",
|
|
91
|
+
url: "https://x/a.pdf"
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
]
|
|
95
|
+
}]);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
//#endregion
|
|
99
|
+
export {};
|
|
100
|
+
|
|
101
|
+
//# sourceMappingURL=codec.test.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codec.test.mjs","names":[],"sources":["../src/codec.test.ts"],"sourcesContent":["import { fileBase64, fileUrl } from \"@effect-uai/core/File\"\nimport { imageBytes } from \"@effect-uai/core/Image\"\nimport * as Items from \"@effect-uai/core/Items\"\nimport { Option, Result } from \"effect\"\nimport { describe, expect, it } from \"vitest\"\nimport { buildRequestBody } from \"./codec.js\"\n\nconst toolResultFor = (output: Items.ToolCallOutput) => {\n const body = Result.getOrThrow(\n buildRequestBody({\n model: \"claude-sonnet-4-5\",\n history: [\n Items.userText(\"go\"),\n { type: \"function_call\", call_id: \"c1\", name: \"shot\", arguments: \"{}\" },\n output,\n ],\n maxTokens: 1024,\n temperature: Option.none(),\n topP: Option.none(),\n topK: Option.none(),\n stopSequences: Option.none(),\n thinking: Option.none(),\n tools: Option.none(),\n toolChoice: Option.none(),\n userId: Option.none(),\n outputConfig: Option.none(),\n cacheControl: Option.none(),\n }),\n )\n return body.messages.at(-1)?.content\n}\n\nconst outputOf = (output: Items.ToolCallOutput[\"output\"], isError?: boolean) =>\n toolResultFor({\n type: \"function_call_output\",\n call_id: \"c1\",\n output,\n ...(isError !== undefined && { isError }),\n })\n\ndescribe(\"tool_result\", () => {\n it(\"sends text and JSON as one string and flags failures with is_error\", () => {\n expect(outputOf([Items.json({ _tag: \"CityNotFound\", city: \"Atlantis\" })], true)).toEqual([\n {\n type: \"tool_result\",\n tool_use_id: \"c1\",\n content: '{\"_tag\":\"CityNotFound\",\"city\":\"Atlantis\"}',\n is_error: true,\n },\n ])\n })\n\n it(\"sends media as image and document blocks, the filename as the document title\", () => {\n expect(\n outputOf([\n Items.text(\"Home\"),\n Items.image(imageBytes(new Uint8Array([1, 2, 3]), \"image/png\")),\n Items.file(fileBase64(\"JVBE\", \"application/pdf\"), \"report.pdf\"),\n Items.file(fileUrl(\"https://x/a.pdf\")),\n ]),\n ).toEqual([\n {\n type: \"tool_result\",\n tool_use_id: \"c1\",\n content: [\n { type: \"text\", text: \"Home\" },\n { type: \"image\", source: { type: \"base64\", media_type: \"image/png\", data: \"AQID\" } },\n {\n type: \"document\",\n source: { type: \"base64\", media_type: \"application/pdf\", data: \"JVBE\" },\n title: \"report.pdf\",\n },\n { type: \"document\", source: { type: \"url\", url: \"https://x/a.pdf\" } },\n ],\n },\n ])\n })\n})\n"],"mappings":";;;;;;;AAOA,MAAM,iBAAiB,WAAiC;CAsBtD,OArBa,OAAO,WAClB,iBAAiB;EACf,OAAO;EACP,SAAS;GACP,MAAM,SAAS,IAAI;GACnB;IAAE,MAAM;IAAiB,SAAS;IAAM,MAAM;IAAQ,WAAW;GAAK;GACtE;EACF;EACA,WAAW;EACX,aAAa,OAAO,KAAK;EACzB,MAAM,OAAO,KAAK;EAClB,MAAM,OAAO,KAAK;EAClB,eAAe,OAAO,KAAK;EAC3B,UAAU,OAAO,KAAK;EACtB,OAAO,OAAO,KAAK;EACnB,YAAY,OAAO,KAAK;EACxB,QAAQ,OAAO,KAAK;EACpB,cAAc,OAAO,KAAK;EAC1B,cAAc,OAAO,KAAK;CAC5B,CAAC,CAEO,CAAC,CAAC,SAAS,GAAG,EAAE,CAAC,EAAE;AAC/B;AAEA,MAAM,YAAY,QAAwC,YACxD,cAAc;CACZ,MAAM;CACN,SAAS;CACT;CACA,GAAI,YAAY,KAAA,KAAa,EAAE,QAAQ;AACzC,CAAC;AAEH,SAAS,qBAAqB;CAC5B,GAAG,4EAA4E;EAC7E,aAAO,SAAS,CAAC,MAAM,KAAK;GAAE,MAAM;GAAgB,MAAM;EAAW,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,QAAQ,CACvF;GACE,MAAM;GACN,aAAa;GACb,SAAS;GACT,UAAU;EACZ,CACF,CAAC;CACH,CAAC;CAED,GAAG,sFAAsF;EACvF,aACE,SAAS;GACP,MAAM,KAAK,MAAM;GACjB,MAAM,MAAM,WAAW,IAAI,WAAW;IAAC;IAAG;IAAG;GAAC,CAAC,GAAG,WAAW,CAAC;GAC9D,MAAM,KAAK,WAAW,QAAQ,iBAAiB,GAAG,YAAY;GAC9D,MAAM,KAAK,QAAQ,iBAAiB,CAAC;EACvC,CAAC,CACH,CAAC,CAAC,QAAQ,CACR;GACE,MAAM;GACN,aAAa;GACb,SAAS;IACP;KAAE,MAAM;KAAQ,MAAM;IAAO;IAC7B;KAAE,MAAM;KAAS,QAAQ;MAAE,MAAM;MAAU,YAAY;MAAa,MAAM;KAAO;IAAE;IACnF;KACE,MAAM;KACN,QAAQ;MAAE,MAAM;MAAU,YAAY;MAAmB,MAAM;KAAO;KACtE,OAAO;IACT;IACA;KAAE,MAAM;KAAY,QAAQ;MAAE,MAAM;MAAO,KAAK;KAAkB;IAAE;GACtE;EACF,CACF,CAAC;CACH,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effect-uai/anthropic",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0-dev-20261110065930",
|
|
4
4
|
"description": "Anthropic Messages API provider for @effect-uai/core.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -43,12 +43,12 @@
|
|
|
43
43
|
"access": "public"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"effect": "4.
|
|
46
|
+
"effect": "4.1.2",
|
|
47
47
|
"typescript": "^7.0.2",
|
|
48
|
-
"@effect-uai/core": "0.
|
|
48
|
+
"@effect-uai/core": "0.19.0-dev-20261110065930"
|
|
49
49
|
},
|
|
50
50
|
"peerDependencies": {
|
|
51
|
-
"@effect-uai/core": "
|
|
51
|
+
"@effect-uai/core": "0.19.0-dev-20261110065930",
|
|
52
52
|
"effect": ">=4.0.1 <5.0.0"
|
|
53
53
|
},
|
|
54
54
|
"scripts": {
|
|
@@ -10,6 +10,7 @@ const localTool = Tool.make({
|
|
|
10
10
|
name: "get_weather",
|
|
11
11
|
description: "Look up the weather.",
|
|
12
12
|
inputSchema: Tool.fromEffectSchema(Schema.Struct({ city: Schema.String })),
|
|
13
|
+
outputSchema: Schema.String,
|
|
13
14
|
run: ({ city }) => Effect.succeed(`sunny in ${city}`),
|
|
14
15
|
})
|
|
15
16
|
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { fileBase64, fileUrl } from "@effect-uai/core/File"
|
|
2
|
+
import { imageBytes } from "@effect-uai/core/Image"
|
|
3
|
+
import * as Items from "@effect-uai/core/Items"
|
|
4
|
+
import { Option, Result } from "effect"
|
|
5
|
+
import { describe, expect, it } from "vitest"
|
|
6
|
+
import { buildRequestBody } from "./codec.js"
|
|
7
|
+
|
|
8
|
+
const toolResultFor = (output: Items.ToolCallOutput) => {
|
|
9
|
+
const body = Result.getOrThrow(
|
|
10
|
+
buildRequestBody({
|
|
11
|
+
model: "claude-sonnet-4-5",
|
|
12
|
+
history: [
|
|
13
|
+
Items.userText("go"),
|
|
14
|
+
{ type: "function_call", call_id: "c1", name: "shot", arguments: "{}" },
|
|
15
|
+
output,
|
|
16
|
+
],
|
|
17
|
+
maxTokens: 1024,
|
|
18
|
+
temperature: Option.none(),
|
|
19
|
+
topP: Option.none(),
|
|
20
|
+
topK: Option.none(),
|
|
21
|
+
stopSequences: Option.none(),
|
|
22
|
+
thinking: Option.none(),
|
|
23
|
+
tools: Option.none(),
|
|
24
|
+
toolChoice: Option.none(),
|
|
25
|
+
userId: Option.none(),
|
|
26
|
+
outputConfig: Option.none(),
|
|
27
|
+
cacheControl: Option.none(),
|
|
28
|
+
}),
|
|
29
|
+
)
|
|
30
|
+
return body.messages.at(-1)?.content
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const outputOf = (output: Items.ToolCallOutput["output"], isError?: boolean) =>
|
|
34
|
+
toolResultFor({
|
|
35
|
+
type: "function_call_output",
|
|
36
|
+
call_id: "c1",
|
|
37
|
+
output,
|
|
38
|
+
...(isError !== undefined && { isError }),
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
describe("tool_result", () => {
|
|
42
|
+
it("sends text and JSON as one string and flags failures with is_error", () => {
|
|
43
|
+
expect(outputOf([Items.json({ _tag: "CityNotFound", city: "Atlantis" })], true)).toEqual([
|
|
44
|
+
{
|
|
45
|
+
type: "tool_result",
|
|
46
|
+
tool_use_id: "c1",
|
|
47
|
+
content: '{"_tag":"CityNotFound","city":"Atlantis"}',
|
|
48
|
+
is_error: true,
|
|
49
|
+
},
|
|
50
|
+
])
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it("sends media as image and document blocks, the filename as the document title", () => {
|
|
54
|
+
expect(
|
|
55
|
+
outputOf([
|
|
56
|
+
Items.text("Home"),
|
|
57
|
+
Items.image(imageBytes(new Uint8Array([1, 2, 3]), "image/png")),
|
|
58
|
+
Items.file(fileBase64("JVBE", "application/pdf"), "report.pdf"),
|
|
59
|
+
Items.file(fileUrl("https://x/a.pdf")),
|
|
60
|
+
]),
|
|
61
|
+
).toEqual([
|
|
62
|
+
{
|
|
63
|
+
type: "tool_result",
|
|
64
|
+
tool_use_id: "c1",
|
|
65
|
+
content: [
|
|
66
|
+
{ type: "text", text: "Home" },
|
|
67
|
+
{ type: "image", source: { type: "base64", media_type: "image/png", data: "AQID" } },
|
|
68
|
+
{
|
|
69
|
+
type: "document",
|
|
70
|
+
source: { type: "base64", media_type: "application/pdf", data: "JVBE" },
|
|
71
|
+
title: "report.pdf",
|
|
72
|
+
},
|
|
73
|
+
{ type: "document", source: { type: "url", url: "https://x/a.pdf" } },
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
])
|
|
77
|
+
})
|
|
78
|
+
})
|
package/src/codec.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Array as Arr, Match, Option, Order, Result, Schema, pipe } from "effect
|
|
|
2
2
|
import { Base64 } from "effect/encoding"
|
|
3
3
|
import * as Items from "@effect-uai/core/Items"
|
|
4
4
|
import { JsonParseError } from "@effect-uai/core/JSONL"
|
|
5
|
+
import * as ToolOutput from "@effect-uai/core/ToolOutput"
|
|
5
6
|
import type { Turn } from "@effect-uai/core/Turn"
|
|
6
7
|
|
|
7
8
|
// ---------------------------------------------------------------------------
|
|
@@ -60,9 +61,20 @@ type RequestTextContent = {
|
|
|
60
61
|
type RequestToolResultContent = {
|
|
61
62
|
readonly type: "tool_result"
|
|
62
63
|
readonly tool_use_id: string
|
|
63
|
-
readonly content: string
|
|
64
|
+
readonly content: string | ReadonlyArray<RequestToolResultBlock>
|
|
65
|
+
readonly is_error?: boolean
|
|
64
66
|
}
|
|
65
67
|
|
|
68
|
+
type RequestDocumentContent = {
|
|
69
|
+
readonly type: "document"
|
|
70
|
+
readonly source:
|
|
71
|
+
| { readonly type: "url"; readonly url: string }
|
|
72
|
+
| { readonly type: "base64"; readonly media_type: string; readonly data: string }
|
|
73
|
+
readonly title?: string
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
type RequestToolResultBlock = RequestTextContent | RequestImageContent | RequestDocumentContent
|
|
77
|
+
|
|
66
78
|
type RequestToolUseContent = {
|
|
67
79
|
readonly type: "tool_use"
|
|
68
80
|
readonly id: string
|
|
@@ -135,6 +147,45 @@ const imageSourceToWire = Match.type<Items.InputImage["source"]>().pipe(
|
|
|
135
147
|
Match.exhaustive,
|
|
136
148
|
)
|
|
137
149
|
|
|
150
|
+
const fileSourceToWire = Match.type<Items.InputFile["source"]>().pipe(
|
|
151
|
+
Match.tag("url", (s): RequestDocumentContent["source"] => ({ type: "url", url: s.url })),
|
|
152
|
+
Match.tag("base64", (s): RequestDocumentContent["source"] => ({
|
|
153
|
+
type: "base64",
|
|
154
|
+
media_type: s.mimeType,
|
|
155
|
+
data: s.base64,
|
|
156
|
+
})),
|
|
157
|
+
Match.tag("bytes", (s): RequestDocumentContent["source"] => ({
|
|
158
|
+
type: "base64",
|
|
159
|
+
media_type: s.mimeType,
|
|
160
|
+
data: Base64.encode(s.bytes),
|
|
161
|
+
})),
|
|
162
|
+
Match.exhaustive,
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
const toolResultBlock = Match.type<Items.ToolOutputContent>().pipe(
|
|
166
|
+
Match.discriminatorsExhaustive("type")({
|
|
167
|
+
input_text: (p): RequestToolResultBlock => ({ type: "text", text: ToolOutput.partText(p) }),
|
|
168
|
+
input_json: (p): RequestToolResultBlock => ({ type: "text", text: ToolOutput.partText(p) }),
|
|
169
|
+
input_image: (p): RequestToolResultBlock => ({
|
|
170
|
+
type: "image",
|
|
171
|
+
source: imageSourceToWire(p.source),
|
|
172
|
+
}),
|
|
173
|
+
input_file: (p): RequestToolResultBlock => ({
|
|
174
|
+
type: "document",
|
|
175
|
+
source: fileSourceToWire(p.source),
|
|
176
|
+
...(p.filename !== undefined && { title: p.filename }),
|
|
177
|
+
}),
|
|
178
|
+
}),
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
// Text and JSON only: one string. With media: image and document blocks.
|
|
182
|
+
const toolResult = (o: Items.ToolCallOutput): RequestToolResultContent => ({
|
|
183
|
+
type: "tool_result",
|
|
184
|
+
tool_use_id: o.call_id,
|
|
185
|
+
content: Option.getOrElse(ToolOutput.text(o.output), () => o.output.map(toolResultBlock)),
|
|
186
|
+
...(o.isError === true && { is_error: true }),
|
|
187
|
+
})
|
|
188
|
+
|
|
138
189
|
const userContentBlock = (
|
|
139
190
|
block: Items.ContentBlock,
|
|
140
191
|
): Result.Result<RequestUserContentBlock, void> =>
|
|
@@ -179,9 +230,7 @@ const itemToUserBlocks = (item: Items.HistoryItem): ReadonlyArray<RequestUserCon
|
|
|
179
230
|
message: (m): ReadonlyArray<RequestUserContentBlock> =>
|
|
180
231
|
m.role === "user" ? pipe(m.content, Arr.filterMap(userContentBlock)) : [],
|
|
181
232
|
function_call: (): ReadonlyArray<RequestUserContentBlock> => [],
|
|
182
|
-
function_call_output: (o): ReadonlyArray<RequestUserContentBlock> => [
|
|
183
|
-
{ type: "tool_result", tool_use_id: o.call_id, content: o.output },
|
|
184
|
-
],
|
|
233
|
+
function_call_output: (o): ReadonlyArray<RequestUserContentBlock> => [toolResult(o)],
|
|
185
234
|
reasoning: (): ReadonlyArray<RequestUserContentBlock> => [],
|
|
186
235
|
}),
|
|
187
236
|
)
|