@ponythewhite/base-context-agent 1.0.0
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/LICENSE +23 -0
- package/NOTICE +22 -0
- package/README.md +551 -0
- package/dist/LICENSE +23 -0
- package/dist/NOTICE +22 -0
- package/dist/agent-loop.d.ts +24 -0
- package/dist/agent-loop.d.ts.map +1 -0
- package/dist/agent-loop.js +897 -0
- package/dist/agent-loop.js.map +1 -0
- package/dist/agent.d.ts +144 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +569 -0
- package/dist/agent.js.map +1 -0
- package/dist/build-info.json +14 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/invocation-output.d.ts +30 -0
- package/dist/invocation-output.d.ts.map +1 -0
- package/dist/invocation-output.js +125 -0
- package/dist/invocation-output.js.map +1 -0
- package/dist/proxy.d.ts +59 -0
- package/dist/proxy.d.ts.map +1 -0
- package/dist/proxy.js +268 -0
- package/dist/proxy.js.map +1 -0
- package/dist/types.d.ts +512 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"sourceCommit": "3b6e414e5d955f03bdebf866f39b85853a896d13",
|
|
3
|
+
"sourceDirty": false,
|
|
4
|
+
"buildHost": {
|
|
5
|
+
"platform": "linux",
|
|
6
|
+
"arch": "x64"
|
|
7
|
+
},
|
|
8
|
+
"toolchain": {
|
|
9
|
+
"node": "v22.12.0",
|
|
10
|
+
"npm": "10.9.0",
|
|
11
|
+
"tsgo": "Version 7.0.0-dev.20260120.1",
|
|
12
|
+
"esbuild": "0.28.1"
|
|
13
|
+
}
|
|
14
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC","sourcesContent":["export * from \"./agent.js\";\nexport * from \"./agent-loop.js\";\nexport { AgentOutputLimitError } from \"./invocation-output.js\";\nexport * from \"./proxy.js\";\nexport * from \"./types.js\";\n"]}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC","sourcesContent":["export * from \"./agent.js\";\nexport * from \"./agent-loop.js\";\nexport { AgentOutputLimitError } from \"./invocation-output.js\";\nexport * from \"./proxy.js\";\nexport * from \"./types.js\";\n"]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { AgentMessage, AgentOutputPolicy, AgentOutputRefusal } from "./types.js";
|
|
2
|
+
/** A resource refusal, never a provider response or an unknown canonical append outcome. */
|
|
3
|
+
export declare class AgentOutputLimitError extends Error {
|
|
4
|
+
readonly refusal: AgentOutputRefusal;
|
|
5
|
+
constructor(refusal: AgentOutputRefusal, options?: ErrorOptions);
|
|
6
|
+
}
|
|
7
|
+
/** The existing invocation result array owns these detached values. No source/receipt store. */
|
|
8
|
+
export declare class InvocationOutput {
|
|
9
|
+
private messages;
|
|
10
|
+
readonly policy: AgentOutputPolicy;
|
|
11
|
+
private sourceBytes;
|
|
12
|
+
refusal?: AgentOutputRefusal;
|
|
13
|
+
private refusalCause?;
|
|
14
|
+
private slots;
|
|
15
|
+
private closed;
|
|
16
|
+
constructor(policy: AgentOutputPolicy, messages: AgentMessage[]);
|
|
17
|
+
private refuse;
|
|
18
|
+
private snapshot;
|
|
19
|
+
checkRoom(count: number): void;
|
|
20
|
+
/** Never throw a quota error through an accepted message/parallel publication. */
|
|
21
|
+
capture(message: AgentMessage): void;
|
|
22
|
+
/** Refresh only values whose original subject this invocation actually captured. */
|
|
23
|
+
refresh(message: AgentMessage): boolean;
|
|
24
|
+
throwIfRefused(): void;
|
|
25
|
+
/** A retained updater or still-live subject cannot keep this invocation's values through the slot owner. */
|
|
26
|
+
dispose(): void;
|
|
27
|
+
/** Callback/terminal consumers own their copy, not the counted collector's aliases. */
|
|
28
|
+
copy(): AgentMessage[];
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=invocation-output.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invocation-output.d.ts","sourceRoot":"","sources":["../src/invocation-output.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEtF,4FAA4F;AAC5F,qBAAa,qBAAsB,SAAQ,KAAK;IAE9C,QAAQ,CAAC,OAAO,EAAE,kBAAkB;IADrC,YACU,OAAO,EAAE,kBAAkB,EACpC,OAAO,CAAC,EAAE,YAAY,EAStB;CACD;AAED,gGAAgG;AAChG,qBAAa,gBAAgB;IAU3B,OAAO,CAAC,QAAQ;IATjB,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAC;IACnC,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAC7B,OAAO,CAAC,YAAY,CAAC,CAAU;IAC/B,OAAO,CAAC,KAAK,CAAyE;IACtF,OAAO,CAAC,MAAM,CAAS;IAEvB,YACC,MAAM,EAAE,iBAAiB,EACjB,QAAQ,EAAE,YAAY,EAAE,EAkBhC;IAED,OAAO,CAAC,MAAM;IAMd,OAAO,CAAC,QAAQ;IAWhB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAG7B;IAED,kFAAkF;IAClF,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAsBnC;IAED,oFAAoF;IACpF,OAAO,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAetC;IAED,cAAc,IAAI,IAAI,CAMrB;IAED,4GAA4G;IAC5G,OAAO,IAAI,IAAI,CAKd;IAED,uFAAuF;IACvF,IAAI,IAAI,YAAY,EAAE,CAGrB;CACD","sourcesContent":["import type { AgentMessage, AgentOutputPolicy, AgentOutputRefusal } from \"./types.js\";\n\n/** A resource refusal, never a provider response or an unknown canonical append outcome. */\nexport class AgentOutputLimitError extends Error {\n\tconstructor(\n\t\treadonly refusal: AgentOutputRefusal,\n\t\toptions?: ErrorOptions,\n\t) {\n\t\tsuper(\n\t\t\trefusal.limit === \"value_encoding\"\n\t\t\t\t? \"Invocation output value cannot be represented as bounded JSON\"\n\t\t\t\t: `Invocation output ${refusal.limit === \"messages\" ? \"message\" : \"source byte\"} limit exceeded`,\n\t\t\toptions,\n\t\t);\n\t\tthis.name = \"AgentOutputLimitError\";\n\t}\n}\n\n/** The existing invocation result array owns these detached values. No source/receipt store. */\nexport class InvocationOutput {\n\treadonly policy: AgentOutputPolicy;\n\tprivate sourceBytes = 2; // JSON array brackets; each later value also pays its separator.\n\trefusal?: AgentOutputRefusal;\n\tprivate refusalCause?: unknown;\n\tprivate slots = new WeakMap<AgentMessage, { index: number; sourceBytes: number }[]>();\n\tprivate closed = false;\n\n\tconstructor(\n\t\tpolicy: AgentOutputPolicy,\n\t\tprivate messages: AgentMessage[],\n\t) {\n\t\tconst limits = { ...policy.limits };\n\t\tif (\n\t\t\t!Number.isSafeInteger(limits.maxMessages) ||\n\t\t\tlimits.maxMessages < 1 ||\n\t\t\t!Number.isSafeInteger(limits.maxSourceBytes) ||\n\t\t\tlimits.maxSourceBytes < 1\n\t\t) {\n\t\t\tthrow new Error(\"Invalid invocation output limits\");\n\t\t}\n\t\tthis.policy = {\n\t\t\tlimits,\n\t\t\tsnapshot: policy.snapshot.bind(policy),\n\t\t\tbindUpdates: policy.bindUpdates?.bind(policy),\n\t\t\tsettleUpdates: policy.settleUpdates?.bind(policy),\n\t\t};\n\t\tif (limits.maxSourceBytes < this.sourceBytes) this.refuse(\"source_bytes\");\n\t}\n\n\tprivate refuse(limit: AgentOutputRefusal[\"limit\"], cause?: unknown): void {\n\t\tif (this.refusal) return;\n\t\tthis.refusal = { kind: \"output_limit\", limit, ...this.policy.limits };\n\t\tthis.refusalCause = cause;\n\t}\n\n\tprivate snapshot(message: AgentMessage, maxBytes: number) {\n\t\ttry {\n\t\t\tconst value = this.policy.snapshot(message, maxBytes);\n\t\t\tif (!value) this.refuse(\"source_bytes\");\n\t\t\treturn value;\n\t\t} catch (error) {\n\t\t\tthis.refuse(\"value_encoding\", error);\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tcheckRoom(count: number): void {\n\t\tif (count > this.policy.limits.maxMessages - this.messages.length) this.refuse(\"messages\");\n\t\tthis.throwIfRefused();\n\t}\n\n\t/** Never throw a quota error through an accepted message/parallel publication. */\n\tcapture(message: AgentMessage): void {\n\t\tif (this.refusal) return;\n\t\tif (this.messages.length === this.policy.limits.maxMessages) {\n\t\t\tthis.refuse(\"messages\");\n\t\t\treturn;\n\t\t}\n\t\tconst separator = this.messages.length === 0 ? 0 : 1;\n\t\tconst remaining = this.policy.limits.maxSourceBytes - this.sourceBytes - separator;\n\t\tif (remaining < 0) {\n\t\t\tthis.refuse(\"source_bytes\");\n\t\t\treturn;\n\t\t}\n\t\tconst value = this.snapshot(message, remaining);\n\t\tif (!value) {\n\t\t\tthis.refuse(\"source_bytes\");\n\t\t\treturn;\n\t\t}\n\t\tthis.sourceBytes += value.sourceBytes + separator;\n\t\tconst slots = this.slots.get(message) ?? [];\n\t\tslots.push({ index: this.messages.length, sourceBytes: value.sourceBytes });\n\t\tthis.slots.set(message, slots);\n\t\tthis.messages.push(value.message);\n\t}\n\n\t/** Refresh only values whose original subject this invocation actually captured. */\n\trefresh(message: AgentMessage): boolean {\n\t\tif (this.closed) return true;\n\t\tif (this.refusal) return false;\n\t\tconst slots = this.slots.get(message);\n\t\tif (!slots) return true;\n\t\tconst previous = slots.reduce((sum, slot) => sum + slot.sourceBytes, 0);\n\t\tconst remaining = this.policy.limits.maxSourceBytes - this.sourceBytes + previous;\n\t\tconst value = this.snapshot(message, Math.floor(remaining / slots.length));\n\t\tif (!value) return false;\n\t\tthis.sourceBytes += value.sourceBytes * slots.length - previous;\n\t\tfor (const slot of slots) {\n\t\t\tthis.messages[slot.index] = value.message;\n\t\t\tslot.sourceBytes = value.sourceBytes;\n\t\t}\n\t\treturn true;\n\t}\n\n\tthrowIfRefused(): void {\n\t\tif (this.refusal)\n\t\t\tthrow new AgentOutputLimitError(\n\t\t\t\t{ ...this.refusal },\n\t\t\t\tthis.refusalCause === undefined ? undefined : { cause: this.refusalCause },\n\t\t\t);\n\t}\n\n\t/** A retained updater or still-live subject cannot keep this invocation's values through the slot owner. */\n\tdispose(): void {\n\t\tthis.closed = true;\n\t\tthis.slots = new WeakMap();\n\t\tthis.messages = [];\n\t\tthis.refusalCause = undefined;\n\t}\n\n\t/** Callback/terminal consumers own their copy, not the counted collector's aliases. */\n\tcopy(): AgentMessage[] {\n\t\tthis.throwIfRefused();\n\t\treturn structuredClone(this.messages);\n\t}\n}\n"]}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/** A resource refusal, never a provider response or an unknown canonical append outcome. */
|
|
2
|
+
export class AgentOutputLimitError extends Error {
|
|
3
|
+
refusal;
|
|
4
|
+
constructor(refusal, options) {
|
|
5
|
+
super(refusal.limit === "value_encoding"
|
|
6
|
+
? "Invocation output value cannot be represented as bounded JSON"
|
|
7
|
+
: `Invocation output ${refusal.limit === "messages" ? "message" : "source byte"} limit exceeded`, options);
|
|
8
|
+
this.refusal = refusal;
|
|
9
|
+
this.name = "AgentOutputLimitError";
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
/** The existing invocation result array owns these detached values. No source/receipt store. */
|
|
13
|
+
export class InvocationOutput {
|
|
14
|
+
messages;
|
|
15
|
+
policy;
|
|
16
|
+
sourceBytes = 2; // JSON array brackets; each later value also pays its separator.
|
|
17
|
+
refusal;
|
|
18
|
+
refusalCause;
|
|
19
|
+
slots = new WeakMap();
|
|
20
|
+
closed = false;
|
|
21
|
+
constructor(policy, messages) {
|
|
22
|
+
this.messages = messages;
|
|
23
|
+
const limits = { ...policy.limits };
|
|
24
|
+
if (!Number.isSafeInteger(limits.maxMessages) ||
|
|
25
|
+
limits.maxMessages < 1 ||
|
|
26
|
+
!Number.isSafeInteger(limits.maxSourceBytes) ||
|
|
27
|
+
limits.maxSourceBytes < 1) {
|
|
28
|
+
throw new Error("Invalid invocation output limits");
|
|
29
|
+
}
|
|
30
|
+
this.policy = {
|
|
31
|
+
limits,
|
|
32
|
+
snapshot: policy.snapshot.bind(policy),
|
|
33
|
+
bindUpdates: policy.bindUpdates?.bind(policy),
|
|
34
|
+
settleUpdates: policy.settleUpdates?.bind(policy),
|
|
35
|
+
};
|
|
36
|
+
if (limits.maxSourceBytes < this.sourceBytes)
|
|
37
|
+
this.refuse("source_bytes");
|
|
38
|
+
}
|
|
39
|
+
refuse(limit, cause) {
|
|
40
|
+
if (this.refusal)
|
|
41
|
+
return;
|
|
42
|
+
this.refusal = { kind: "output_limit", limit, ...this.policy.limits };
|
|
43
|
+
this.refusalCause = cause;
|
|
44
|
+
}
|
|
45
|
+
snapshot(message, maxBytes) {
|
|
46
|
+
try {
|
|
47
|
+
const value = this.policy.snapshot(message, maxBytes);
|
|
48
|
+
if (!value)
|
|
49
|
+
this.refuse("source_bytes");
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
this.refuse("value_encoding", error);
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
checkRoom(count) {
|
|
58
|
+
if (count > this.policy.limits.maxMessages - this.messages.length)
|
|
59
|
+
this.refuse("messages");
|
|
60
|
+
this.throwIfRefused();
|
|
61
|
+
}
|
|
62
|
+
/** Never throw a quota error through an accepted message/parallel publication. */
|
|
63
|
+
capture(message) {
|
|
64
|
+
if (this.refusal)
|
|
65
|
+
return;
|
|
66
|
+
if (this.messages.length === this.policy.limits.maxMessages) {
|
|
67
|
+
this.refuse("messages");
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const separator = this.messages.length === 0 ? 0 : 1;
|
|
71
|
+
const remaining = this.policy.limits.maxSourceBytes - this.sourceBytes - separator;
|
|
72
|
+
if (remaining < 0) {
|
|
73
|
+
this.refuse("source_bytes");
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const value = this.snapshot(message, remaining);
|
|
77
|
+
if (!value) {
|
|
78
|
+
this.refuse("source_bytes");
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
this.sourceBytes += value.sourceBytes + separator;
|
|
82
|
+
const slots = this.slots.get(message) ?? [];
|
|
83
|
+
slots.push({ index: this.messages.length, sourceBytes: value.sourceBytes });
|
|
84
|
+
this.slots.set(message, slots);
|
|
85
|
+
this.messages.push(value.message);
|
|
86
|
+
}
|
|
87
|
+
/** Refresh only values whose original subject this invocation actually captured. */
|
|
88
|
+
refresh(message) {
|
|
89
|
+
if (this.closed)
|
|
90
|
+
return true;
|
|
91
|
+
if (this.refusal)
|
|
92
|
+
return false;
|
|
93
|
+
const slots = this.slots.get(message);
|
|
94
|
+
if (!slots)
|
|
95
|
+
return true;
|
|
96
|
+
const previous = slots.reduce((sum, slot) => sum + slot.sourceBytes, 0);
|
|
97
|
+
const remaining = this.policy.limits.maxSourceBytes - this.sourceBytes + previous;
|
|
98
|
+
const value = this.snapshot(message, Math.floor(remaining / slots.length));
|
|
99
|
+
if (!value)
|
|
100
|
+
return false;
|
|
101
|
+
this.sourceBytes += value.sourceBytes * slots.length - previous;
|
|
102
|
+
for (const slot of slots) {
|
|
103
|
+
this.messages[slot.index] = value.message;
|
|
104
|
+
slot.sourceBytes = value.sourceBytes;
|
|
105
|
+
}
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
throwIfRefused() {
|
|
109
|
+
if (this.refusal)
|
|
110
|
+
throw new AgentOutputLimitError({ ...this.refusal }, this.refusalCause === undefined ? undefined : { cause: this.refusalCause });
|
|
111
|
+
}
|
|
112
|
+
/** A retained updater or still-live subject cannot keep this invocation's values through the slot owner. */
|
|
113
|
+
dispose() {
|
|
114
|
+
this.closed = true;
|
|
115
|
+
this.slots = new WeakMap();
|
|
116
|
+
this.messages = [];
|
|
117
|
+
this.refusalCause = undefined;
|
|
118
|
+
}
|
|
119
|
+
/** Callback/terminal consumers own their copy, not the counted collector's aliases. */
|
|
120
|
+
copy() {
|
|
121
|
+
this.throwIfRefused();
|
|
122
|
+
return structuredClone(this.messages);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=invocation-output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"invocation-output.js","sourceRoot":"","sources":["../src/invocation-output.ts"],"names":[],"mappings":"AAEA,4FAA4F;AAC5F,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAErC,OAAO;IADjB,YACU,OAA2B,EACpC,OAAsB,EACrB;QACD,KAAK,CACJ,OAAO,CAAC,KAAK,KAAK,gBAAgB;YACjC,CAAC,CAAC,+DAA+D;YACjE,CAAC,CAAC,qBAAqB,OAAO,CAAC,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,aAAa,iBAAiB,EACjG,OAAO,CACP,CAAC;uBARO,OAAO;QAShB,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IAAA,CACpC;CACD;AAED,gGAAgG;AAChG,MAAM,OAAO,gBAAgB;IAUnB,QAAQ;IATR,MAAM,CAAoB;IAC3B,WAAW,GAAG,CAAC,CAAC,CAAC,iEAAiE;IAC1F,OAAO,CAAsB;IACrB,YAAY,CAAW;IACvB,KAAK,GAAG,IAAI,OAAO,EAA0D,CAAC;IAC9E,MAAM,GAAG,KAAK,CAAC;IAEvB,YACC,MAAyB,EACjB,QAAwB,EAC/B;wBADO,QAAQ;QAEhB,MAAM,MAAM,GAAG,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;QACpC,IACC,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,WAAW,CAAC;YACzC,MAAM,CAAC,WAAW,GAAG,CAAC;YACtB,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,cAAc,CAAC;YAC5C,MAAM,CAAC,cAAc,GAAG,CAAC,EACxB,CAAC;YACF,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;QACrD,CAAC;QACD,IAAI,CAAC,MAAM,GAAG;YACb,MAAM;YACN,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC;YACtC,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC,MAAM,CAAC;YAC7C,aAAa,EAAE,MAAM,CAAC,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC;SACjD,CAAC;QACF,IAAI,MAAM,CAAC,cAAc,GAAG,IAAI,CAAC,WAAW;YAAE,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;IAAA,CAC1E;IAEO,MAAM,CAAC,KAAkC,EAAE,KAAe,EAAQ;QACzE,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QACzB,IAAI,CAAC,OAAO,GAAG,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACtE,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;IAAA,CAC1B;IAEO,QAAQ,CAAC,OAAqB,EAAE,QAAgB,EAAE;QACzD,IAAI,CAAC;YACJ,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;YACtD,IAAI,CAAC,KAAK;gBAAE,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;YACxC,OAAO,KAAK,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,MAAM,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC;YACrC,OAAO,SAAS,CAAC;QAClB,CAAC;IAAA,CACD;IAED,SAAS,CAAC,KAAa,EAAQ;QAC9B,IAAI,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM;YAAE,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAC3F,IAAI,CAAC,cAAc,EAAE,CAAC;IAAA,CACtB;IAED,kFAAkF;IAClF,OAAO,CAAC,OAAqB,EAAQ;QACpC,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QACzB,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YAC7D,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YACxB,OAAO;QACR,CAAC;QACD,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACrD,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,cAAc,GAAG,IAAI,CAAC,WAAW,GAAG,SAAS,CAAC;QACnF,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;YACnB,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;YAC5B,OAAO;QACR,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;QAChD,IAAI,CAAC,KAAK,EAAE,CAAC;YACZ,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;YAC5B,OAAO;QACR,CAAC;QACD,IAAI,CAAC,WAAW,IAAI,KAAK,CAAC,WAAW,GAAG,SAAS,CAAC;QAClD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QAC5C,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;QAC5E,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAC/B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAAA,CAClC;IAED,oFAAoF;IACpF,OAAO,CAAC,OAAqB,EAAW;QACvC,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QAC7B,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO,KAAK,CAAC;QAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACtC,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QACxB,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;QACxE,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,cAAc,GAAG,IAAI,CAAC,WAAW,GAAG,QAAQ,CAAC;QAClF,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;QAC3E,IAAI,CAAC,KAAK;YAAE,OAAO,KAAK,CAAC;QACzB,IAAI,CAAC,WAAW,IAAI,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC,MAAM,GAAG,QAAQ,CAAC;QAChE,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YAC1B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC;YAC1C,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC;QACtC,CAAC;QACD,OAAO,IAAI,CAAC;IAAA,CACZ;IAED,cAAc,GAAS;QACtB,IAAI,IAAI,CAAC,OAAO;YACf,MAAM,IAAI,qBAAqB,CAC9B,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,EACnB,IAAI,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,CAC1E,CAAC;IAAA,CACH;IAED,4GAA4G;IAC5G,OAAO,GAAS;QACf,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,IAAI,CAAC,KAAK,GAAG,IAAI,OAAO,EAAE,CAAC;QAC3B,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;QACnB,IAAI,CAAC,YAAY,GAAG,SAAS,CAAC;IAAA,CAC9B;IAED,uFAAuF;IACvF,IAAI,GAAmB;QACtB,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,OAAO,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAAA,CACtC;CACD","sourcesContent":["import type { AgentMessage, AgentOutputPolicy, AgentOutputRefusal } from \"./types.js\";\n\n/** A resource refusal, never a provider response or an unknown canonical append outcome. */\nexport class AgentOutputLimitError extends Error {\n\tconstructor(\n\t\treadonly refusal: AgentOutputRefusal,\n\t\toptions?: ErrorOptions,\n\t) {\n\t\tsuper(\n\t\t\trefusal.limit === \"value_encoding\"\n\t\t\t\t? \"Invocation output value cannot be represented as bounded JSON\"\n\t\t\t\t: `Invocation output ${refusal.limit === \"messages\" ? \"message\" : \"source byte\"} limit exceeded`,\n\t\t\toptions,\n\t\t);\n\t\tthis.name = \"AgentOutputLimitError\";\n\t}\n}\n\n/** The existing invocation result array owns these detached values. No source/receipt store. */\nexport class InvocationOutput {\n\treadonly policy: AgentOutputPolicy;\n\tprivate sourceBytes = 2; // JSON array brackets; each later value also pays its separator.\n\trefusal?: AgentOutputRefusal;\n\tprivate refusalCause?: unknown;\n\tprivate slots = new WeakMap<AgentMessage, { index: number; sourceBytes: number }[]>();\n\tprivate closed = false;\n\n\tconstructor(\n\t\tpolicy: AgentOutputPolicy,\n\t\tprivate messages: AgentMessage[],\n\t) {\n\t\tconst limits = { ...policy.limits };\n\t\tif (\n\t\t\t!Number.isSafeInteger(limits.maxMessages) ||\n\t\t\tlimits.maxMessages < 1 ||\n\t\t\t!Number.isSafeInteger(limits.maxSourceBytes) ||\n\t\t\tlimits.maxSourceBytes < 1\n\t\t) {\n\t\t\tthrow new Error(\"Invalid invocation output limits\");\n\t\t}\n\t\tthis.policy = {\n\t\t\tlimits,\n\t\t\tsnapshot: policy.snapshot.bind(policy),\n\t\t\tbindUpdates: policy.bindUpdates?.bind(policy),\n\t\t\tsettleUpdates: policy.settleUpdates?.bind(policy),\n\t\t};\n\t\tif (limits.maxSourceBytes < this.sourceBytes) this.refuse(\"source_bytes\");\n\t}\n\n\tprivate refuse(limit: AgentOutputRefusal[\"limit\"], cause?: unknown): void {\n\t\tif (this.refusal) return;\n\t\tthis.refusal = { kind: \"output_limit\", limit, ...this.policy.limits };\n\t\tthis.refusalCause = cause;\n\t}\n\n\tprivate snapshot(message: AgentMessage, maxBytes: number) {\n\t\ttry {\n\t\t\tconst value = this.policy.snapshot(message, maxBytes);\n\t\t\tif (!value) this.refuse(\"source_bytes\");\n\t\t\treturn value;\n\t\t} catch (error) {\n\t\t\tthis.refuse(\"value_encoding\", error);\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tcheckRoom(count: number): void {\n\t\tif (count > this.policy.limits.maxMessages - this.messages.length) this.refuse(\"messages\");\n\t\tthis.throwIfRefused();\n\t}\n\n\t/** Never throw a quota error through an accepted message/parallel publication. */\n\tcapture(message: AgentMessage): void {\n\t\tif (this.refusal) return;\n\t\tif (this.messages.length === this.policy.limits.maxMessages) {\n\t\t\tthis.refuse(\"messages\");\n\t\t\treturn;\n\t\t}\n\t\tconst separator = this.messages.length === 0 ? 0 : 1;\n\t\tconst remaining = this.policy.limits.maxSourceBytes - this.sourceBytes - separator;\n\t\tif (remaining < 0) {\n\t\t\tthis.refuse(\"source_bytes\");\n\t\t\treturn;\n\t\t}\n\t\tconst value = this.snapshot(message, remaining);\n\t\tif (!value) {\n\t\t\tthis.refuse(\"source_bytes\");\n\t\t\treturn;\n\t\t}\n\t\tthis.sourceBytes += value.sourceBytes + separator;\n\t\tconst slots = this.slots.get(message) ?? [];\n\t\tslots.push({ index: this.messages.length, sourceBytes: value.sourceBytes });\n\t\tthis.slots.set(message, slots);\n\t\tthis.messages.push(value.message);\n\t}\n\n\t/** Refresh only values whose original subject this invocation actually captured. */\n\trefresh(message: AgentMessage): boolean {\n\t\tif (this.closed) return true;\n\t\tif (this.refusal) return false;\n\t\tconst slots = this.slots.get(message);\n\t\tif (!slots) return true;\n\t\tconst previous = slots.reduce((sum, slot) => sum + slot.sourceBytes, 0);\n\t\tconst remaining = this.policy.limits.maxSourceBytes - this.sourceBytes + previous;\n\t\tconst value = this.snapshot(message, Math.floor(remaining / slots.length));\n\t\tif (!value) return false;\n\t\tthis.sourceBytes += value.sourceBytes * slots.length - previous;\n\t\tfor (const slot of slots) {\n\t\t\tthis.messages[slot.index] = value.message;\n\t\t\tslot.sourceBytes = value.sourceBytes;\n\t\t}\n\t\treturn true;\n\t}\n\n\tthrowIfRefused(): void {\n\t\tif (this.refusal)\n\t\t\tthrow new AgentOutputLimitError(\n\t\t\t\t{ ...this.refusal },\n\t\t\t\tthis.refusalCause === undefined ? undefined : { cause: this.refusalCause },\n\t\t\t);\n\t}\n\n\t/** A retained updater or still-live subject cannot keep this invocation's values through the slot owner. */\n\tdispose(): void {\n\t\tthis.closed = true;\n\t\tthis.slots = new WeakMap();\n\t\tthis.messages = [];\n\t\tthis.refusalCause = undefined;\n\t}\n\n\t/** Callback/terminal consumers own their copy, not the counted collector's aliases. */\n\tcopy(): AgentMessage[] {\n\t\tthis.throwIfRefused();\n\t\treturn structuredClone(this.messages);\n\t}\n}\n"]}
|
package/dist/proxy.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type AssistantMessage, type AssistantMessageEvent, type Context, EventStream, type Model, type SimpleStreamOptions, type StopReason } from "@ponythewhite/base-context-ai";
|
|
2
|
+
declare class ProxyMessageEventStream extends EventStream<AssistantMessageEvent, AssistantMessage> {
|
|
3
|
+
constructor();
|
|
4
|
+
}
|
|
5
|
+
export type ProxyAssistantMessageEvent = {
|
|
6
|
+
type: "start";
|
|
7
|
+
} | {
|
|
8
|
+
type: "text_start";
|
|
9
|
+
contentIndex: number;
|
|
10
|
+
} | {
|
|
11
|
+
type: "text_delta";
|
|
12
|
+
contentIndex: number;
|
|
13
|
+
delta: string;
|
|
14
|
+
} | {
|
|
15
|
+
type: "text_end";
|
|
16
|
+
contentIndex: number;
|
|
17
|
+
contentSignature?: string;
|
|
18
|
+
} | {
|
|
19
|
+
type: "thinking_start";
|
|
20
|
+
contentIndex: number;
|
|
21
|
+
} | {
|
|
22
|
+
type: "thinking_delta";
|
|
23
|
+
contentIndex: number;
|
|
24
|
+
delta: string;
|
|
25
|
+
} | {
|
|
26
|
+
type: "thinking_end";
|
|
27
|
+
contentIndex: number;
|
|
28
|
+
contentSignature?: string;
|
|
29
|
+
} | {
|
|
30
|
+
type: "toolcall_start";
|
|
31
|
+
contentIndex: number;
|
|
32
|
+
id: string;
|
|
33
|
+
toolName: string;
|
|
34
|
+
} | {
|
|
35
|
+
type: "toolcall_delta";
|
|
36
|
+
contentIndex: number;
|
|
37
|
+
delta: string;
|
|
38
|
+
} | {
|
|
39
|
+
type: "toolcall_end";
|
|
40
|
+
contentIndex: number;
|
|
41
|
+
} | {
|
|
42
|
+
type: "done";
|
|
43
|
+
reason: Extract<StopReason, "stop" | "length" | "toolUse">;
|
|
44
|
+
usage: AssistantMessage["usage"];
|
|
45
|
+
} | {
|
|
46
|
+
type: "error";
|
|
47
|
+
reason: Extract<StopReason, "aborted" | "error">;
|
|
48
|
+
errorMessage?: string;
|
|
49
|
+
usage: AssistantMessage["usage"];
|
|
50
|
+
};
|
|
51
|
+
type ProxySerializableStreamOptions = Pick<SimpleStreamOptions, "temperature" | "maxTokens" | "reasoning" | "cacheRetention" | "sessionId" | "headers" | "metadata" | "transport" | "thinkingBudgets" | "maxRetryDelayMs">;
|
|
52
|
+
export interface ProxyStreamOptions extends ProxySerializableStreamOptions {
|
|
53
|
+
signal?: AbortSignal;
|
|
54
|
+
authToken: string;
|
|
55
|
+
proxyUrl: string;
|
|
56
|
+
}
|
|
57
|
+
export declare function streamProxy(model: Model<any>, context: Context, options: ProxyStreamOptions): ProxyMessageEventStream;
|
|
58
|
+
export {};
|
|
59
|
+
//# sourceMappingURL=proxy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,OAAO,EACZ,WAAW,EACX,KAAK,KAAK,EAEV,KAAK,mBAAmB,EACxB,KAAK,UAAU,EAEf,MAAM,+BAA+B,CAAC;AAEvC,cAAM,uBAAwB,SAAQ,WAAW,CAAC,qBAAqB,EAAE,gBAAgB,CAAC;IACzF,cASC;CACD;AAED,MAAM,MAAM,0BAA0B,GACnC;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAC5C;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC3D;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,GACrE;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAChD;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC/D;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,GACzE;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC9E;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC/D;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAC9C;IACA,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC,CAAC;IAC3D,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;CAChC,GACD;IACA,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,OAAO,CAAC,UAAU,EAAE,SAAS,GAAG,OAAO,CAAC,CAAC;IACjD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;CAChC,CAAC;AAEL,KAAK,8BAA8B,GAAG,IAAI,CACzC,mBAAmB,EACjB,aAAa,GACb,WAAW,GACX,WAAW,GACX,gBAAgB,GAChB,WAAW,GACX,SAAS,GACT,UAAU,GACV,WAAW,GACX,iBAAiB,GACjB,iBAAiB,CACnB,CAAC;AAEF,MAAM,WAAW,kBAAmB,SAAQ,8BAA8B;IACzE,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CACjB;AAoCD,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,kBAAkB,GAAG,uBAAuB,CAoHrH","sourcesContent":["import {\n\ttype AssistantMessage,\n\ttype AssistantMessageEvent,\n\ttype Context,\n\tEventStream,\n\ttype Model,\n\tparseStreamingJson,\n\ttype SimpleStreamOptions,\n\ttype StopReason,\n\ttype ToolCall,\n} from \"@ponythewhite/base-context-ai\";\n\nclass ProxyMessageEventStream extends EventStream<AssistantMessageEvent, AssistantMessage> {\n\tconstructor() {\n\t\tsuper(\n\t\t\t(event) => event.type === \"done\" || event.type === \"error\",\n\t\t\t(event) => {\n\t\t\t\tif (event.type === \"done\") return event.message;\n\t\t\t\tif (event.type === \"error\") return event.error;\n\t\t\t\tthrow new Error(\"Unexpected event type\");\n\t\t\t},\n\t\t);\n\t}\n}\n\nexport type ProxyAssistantMessageEvent =\n\t| { type: \"start\" }\n\t| { type: \"text_start\"; contentIndex: number }\n\t| { type: \"text_delta\"; contentIndex: number; delta: string }\n\t| { type: \"text_end\"; contentIndex: number; contentSignature?: string }\n\t| { type: \"thinking_start\"; contentIndex: number }\n\t| { type: \"thinking_delta\"; contentIndex: number; delta: string }\n\t| { type: \"thinking_end\"; contentIndex: number; contentSignature?: string }\n\t| { type: \"toolcall_start\"; contentIndex: number; id: string; toolName: string }\n\t| { type: \"toolcall_delta\"; contentIndex: number; delta: string }\n\t| { type: \"toolcall_end\"; contentIndex: number }\n\t| {\n\t\t\ttype: \"done\";\n\t\t\treason: Extract<StopReason, \"stop\" | \"length\" | \"toolUse\">;\n\t\t\tusage: AssistantMessage[\"usage\"];\n\t }\n\t| {\n\t\t\ttype: \"error\";\n\t\t\treason: Extract<StopReason, \"aborted\" | \"error\">;\n\t\t\terrorMessage?: string;\n\t\t\tusage: AssistantMessage[\"usage\"];\n\t };\n\ntype ProxySerializableStreamOptions = Pick<\n\tSimpleStreamOptions,\n\t| \"temperature\"\n\t| \"maxTokens\"\n\t| \"reasoning\"\n\t| \"cacheRetention\"\n\t| \"sessionId\"\n\t| \"headers\"\n\t| \"metadata\"\n\t| \"transport\"\n\t| \"thinkingBudgets\"\n\t| \"maxRetryDelayMs\"\n>;\n\nexport interface ProxyStreamOptions extends ProxySerializableStreamOptions {\n\tsignal?: AbortSignal;\n\tauthToken: string;\n\tproxyUrl: string;\n}\n\n/**\n * Stream function that proxies through a server instead of calling LLM providers directly.\n * The server strips the partial field from delta events to reduce bandwidth.\n * We reconstruct the partial message client-side.\n *\n * Use this as the `streamFn` option when creating an Agent that needs to go through a proxy.\n *\n * @example\n * ```typescript\n * const agent = new Agent({\n * streamFn: (model, context, options) =>\n * streamProxy(model, context, {\n * ...options,\n * authToken: await getAuthToken(),\n * proxyUrl: \"https://genai.example.com\",\n * }),\n * });\n * ```\n */\nfunction buildProxyRequestOptions(options: ProxyStreamOptions): ProxySerializableStreamOptions {\n\treturn {\n\t\ttemperature: options.temperature,\n\t\tmaxTokens: options.maxTokens,\n\t\treasoning: options.reasoning,\n\t\tcacheRetention: options.cacheRetention,\n\t\tsessionId: options.sessionId,\n\t\theaders: options.headers,\n\t\tmetadata: options.metadata,\n\t\ttransport: options.transport,\n\t\tthinkingBudgets: options.thinkingBudgets,\n\t\tmaxRetryDelayMs: options.maxRetryDelayMs,\n\t};\n}\n\nexport function streamProxy(model: Model<any>, context: Context, options: ProxyStreamOptions): ProxyMessageEventStream {\n\tconst stream = new ProxyMessageEventStream();\n\n\t(async () => {\n\t\tconst partial: AssistantMessage = {\n\t\t\trole: \"assistant\",\n\t\t\tstopReason: \"stop\",\n\t\t\tcontent: [],\n\t\t\tapi: model.api,\n\t\t\tprovider: model.provider,\n\t\t\tmodel: model.id,\n\t\t\tusage: {\n\t\t\t\tinput: 0,\n\t\t\t\toutput: 0,\n\t\t\t\tcacheRead: 0,\n\t\t\t\tcacheWrite: 0,\n\t\t\t\ttotalTokens: 0,\n\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t},\n\t\t\ttimestamp: Date.now(),\n\t\t};\n\n\t\tlet reader: ReadableStreamDefaultReader<Uint8Array> | undefined;\n\n\t\tconst abortHandler = () => {\n\t\t\tif (reader) {\n\t\t\t\treader.cancel(\"Request aborted by user\").catch(() => {});\n\t\t\t}\n\t\t};\n\n\t\tif (options.signal) {\n\t\t\toptions.signal.addEventListener(\"abort\", abortHandler);\n\t\t}\n\n\t\ttry {\n\t\t\tconst response = await fetch(`${options.proxyUrl}/api/stream`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${options.authToken}`,\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tmodel,\n\t\t\t\t\tcontext,\n\t\t\t\t\toptions: buildProxyRequestOptions(options),\n\t\t\t\t}),\n\t\t\t\tsignal: options.signal,\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tlet errorMessage = `Proxy error: ${response.status} ${response.statusText}`;\n\t\t\t\ttry {\n\t\t\t\t\tconst errorData = (await response.json()) as { error?: string };\n\t\t\t\t\tif (errorData.error) {\n\t\t\t\t\t\terrorMessage = `Proxy error: ${errorData.error}`;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Keep the status-text fallback when the error body is not JSON.\n\t\t\t\t}\n\t\t\t\tthrow new Error(errorMessage);\n\t\t\t}\n\n\t\t\treader = response.body!.getReader();\n\t\t\tconst decoder = new TextDecoder();\n\t\t\tlet buffer = \"\";\n\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\tif (done) break;\n\n\t\t\t\tif (options.signal?.aborted) {\n\t\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t\t}\n\n\t\t\t\tbuffer += decoder.decode(value, { stream: true });\n\t\t\t\tconst lines = buffer.split(\"\\n\");\n\t\t\t\tbuffer = lines.pop() || \"\";\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tif (line.startsWith(\"data: \")) {\n\t\t\t\t\t\tconst data = line.slice(6).trim();\n\t\t\t\t\t\tif (data) {\n\t\t\t\t\t\t\tconst proxyEvent = JSON.parse(data) as ProxyAssistantMessageEvent;\n\t\t\t\t\t\t\tconst event = processProxyEvent(proxyEvent, partial);\n\t\t\t\t\t\t\tif (event) {\n\t\t\t\t\t\t\t\tstream.push(event);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (options.signal?.aborted) {\n\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t}\n\n\t\t\tstream.end();\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : String(error);\n\t\t\tconst reason = options.signal?.aborted ? \"aborted\" : \"error\";\n\t\t\tpartial.stopReason = reason;\n\t\t\tpartial.errorMessage = errorMessage;\n\t\t\tstream.push({\n\t\t\t\ttype: \"error\",\n\t\t\t\treason,\n\t\t\t\terror: partial,\n\t\t\t});\n\t\t\tstream.end();\n\t\t} finally {\n\t\t\tif (options.signal) {\n\t\t\t\toptions.signal.removeEventListener(\"abort\", abortHandler);\n\t\t\t}\n\t\t}\n\t})();\n\n\treturn stream;\n}\n\nfunction processProxyEvent(\n\tproxyEvent: ProxyAssistantMessageEvent,\n\tpartial: AssistantMessage,\n): AssistantMessageEvent | undefined {\n\tswitch (proxyEvent.type) {\n\t\tcase \"start\":\n\t\t\treturn { type: \"start\", partial };\n\n\t\tcase \"text_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = { type: \"text\", text: \"\" };\n\t\t\treturn { type: \"text_start\", contentIndex: proxyEvent.contentIndex, partial };\n\n\t\tcase \"text_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"text\") {\n\t\t\t\tcontent.text += proxyEvent.delta;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"text_delta\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received text_delta for non-text content\");\n\t\t}\n\n\t\tcase \"text_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"text\") {\n\t\t\t\tcontent.textSignature = proxyEvent.contentSignature;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"text_end\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tcontent: content.text,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received text_end for non-text content\");\n\t\t}\n\n\t\tcase \"thinking_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = { type: \"thinking\", thinking: \"\" };\n\t\t\treturn { type: \"thinking_start\", contentIndex: proxyEvent.contentIndex, partial };\n\n\t\tcase \"thinking_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\tcontent.thinking += proxyEvent.delta;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"thinking_delta\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received thinking_delta for non-thinking content\");\n\t\t}\n\n\t\tcase \"thinking_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\tcontent.thinkingSignature = proxyEvent.contentSignature;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"thinking_end\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tcontent: content.thinking,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received thinking_end for non-thinking content\");\n\t\t}\n\n\t\tcase \"toolcall_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = {\n\t\t\t\ttype: \"toolCall\",\n\t\t\t\tid: proxyEvent.id,\n\t\t\t\tname: proxyEvent.toolName,\n\t\t\t\targuments: {},\n\t\t\t\tpartialJson: \"\",\n\t\t\t} satisfies ToolCall & { partialJson: string } as ToolCall;\n\t\t\treturn { type: \"toolcall_start\", contentIndex: proxyEvent.contentIndex, partial };\n\n\t\tcase \"toolcall_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\t(content as any).partialJson += proxyEvent.delta;\n\t\t\t\tcontent.arguments = parseStreamingJson((content as any).partialJson) || {};\n\t\t\t\tpartial.content[proxyEvent.contentIndex] = { ...content }; // Trigger reactivity\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"toolcall_delta\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received toolcall_delta for non-toolCall content\");\n\t\t}\n\n\t\tcase \"toolcall_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\tdelete (content as any).partialJson;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"toolcall_end\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\ttoolCall: content,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\n\t\tcase \"done\":\n\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\treturn { type: \"done\", reason: proxyEvent.reason, message: partial };\n\n\t\tcase \"error\":\n\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\tpartial.errorMessage = proxyEvent.errorMessage;\n\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\treturn { type: \"error\", reason: proxyEvent.reason, error: partial };\n\n\t\tdefault: {\n\t\t\tconst _exhaustiveCheck: never = proxyEvent;\n\t\t\tconsole.warn(`Unhandled proxy event type: ${(proxyEvent as any).type}`);\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n"]}
|
package/dist/proxy.js
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
import { EventStream, parseStreamingJson, } from "@ponythewhite/base-context-ai";
|
|
2
|
+
class ProxyMessageEventStream extends EventStream {
|
|
3
|
+
constructor() {
|
|
4
|
+
super((event) => event.type === "done" || event.type === "error", (event) => {
|
|
5
|
+
if (event.type === "done")
|
|
6
|
+
return event.message;
|
|
7
|
+
if (event.type === "error")
|
|
8
|
+
return event.error;
|
|
9
|
+
throw new Error("Unexpected event type");
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Stream function that proxies through a server instead of calling LLM providers directly.
|
|
15
|
+
* The server strips the partial field from delta events to reduce bandwidth.
|
|
16
|
+
* We reconstruct the partial message client-side.
|
|
17
|
+
*
|
|
18
|
+
* Use this as the `streamFn` option when creating an Agent that needs to go through a proxy.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const agent = new Agent({
|
|
23
|
+
* streamFn: (model, context, options) =>
|
|
24
|
+
* streamProxy(model, context, {
|
|
25
|
+
* ...options,
|
|
26
|
+
* authToken: await getAuthToken(),
|
|
27
|
+
* proxyUrl: "https://genai.example.com",
|
|
28
|
+
* }),
|
|
29
|
+
* });
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
function buildProxyRequestOptions(options) {
|
|
33
|
+
return {
|
|
34
|
+
temperature: options.temperature,
|
|
35
|
+
maxTokens: options.maxTokens,
|
|
36
|
+
reasoning: options.reasoning,
|
|
37
|
+
cacheRetention: options.cacheRetention,
|
|
38
|
+
sessionId: options.sessionId,
|
|
39
|
+
headers: options.headers,
|
|
40
|
+
metadata: options.metadata,
|
|
41
|
+
transport: options.transport,
|
|
42
|
+
thinkingBudgets: options.thinkingBudgets,
|
|
43
|
+
maxRetryDelayMs: options.maxRetryDelayMs,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export function streamProxy(model, context, options) {
|
|
47
|
+
const stream = new ProxyMessageEventStream();
|
|
48
|
+
(async () => {
|
|
49
|
+
const partial = {
|
|
50
|
+
role: "assistant",
|
|
51
|
+
stopReason: "stop",
|
|
52
|
+
content: [],
|
|
53
|
+
api: model.api,
|
|
54
|
+
provider: model.provider,
|
|
55
|
+
model: model.id,
|
|
56
|
+
usage: {
|
|
57
|
+
input: 0,
|
|
58
|
+
output: 0,
|
|
59
|
+
cacheRead: 0,
|
|
60
|
+
cacheWrite: 0,
|
|
61
|
+
totalTokens: 0,
|
|
62
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
63
|
+
},
|
|
64
|
+
timestamp: Date.now(),
|
|
65
|
+
};
|
|
66
|
+
let reader;
|
|
67
|
+
const abortHandler = () => {
|
|
68
|
+
if (reader) {
|
|
69
|
+
reader.cancel("Request aborted by user").catch(() => { });
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
if (options.signal) {
|
|
73
|
+
options.signal.addEventListener("abort", abortHandler);
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
const response = await fetch(`${options.proxyUrl}/api/stream`, {
|
|
77
|
+
method: "POST",
|
|
78
|
+
headers: {
|
|
79
|
+
Authorization: `Bearer ${options.authToken}`,
|
|
80
|
+
"Content-Type": "application/json",
|
|
81
|
+
},
|
|
82
|
+
body: JSON.stringify({
|
|
83
|
+
model,
|
|
84
|
+
context,
|
|
85
|
+
options: buildProxyRequestOptions(options),
|
|
86
|
+
}),
|
|
87
|
+
signal: options.signal,
|
|
88
|
+
});
|
|
89
|
+
if (!response.ok) {
|
|
90
|
+
let errorMessage = `Proxy error: ${response.status} ${response.statusText}`;
|
|
91
|
+
try {
|
|
92
|
+
const errorData = (await response.json());
|
|
93
|
+
if (errorData.error) {
|
|
94
|
+
errorMessage = `Proxy error: ${errorData.error}`;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// Keep the status-text fallback when the error body is not JSON.
|
|
99
|
+
}
|
|
100
|
+
throw new Error(errorMessage);
|
|
101
|
+
}
|
|
102
|
+
reader = response.body.getReader();
|
|
103
|
+
const decoder = new TextDecoder();
|
|
104
|
+
let buffer = "";
|
|
105
|
+
while (true) {
|
|
106
|
+
const { done, value } = await reader.read();
|
|
107
|
+
if (done)
|
|
108
|
+
break;
|
|
109
|
+
if (options.signal?.aborted) {
|
|
110
|
+
throw new Error("Request aborted by user");
|
|
111
|
+
}
|
|
112
|
+
buffer += decoder.decode(value, { stream: true });
|
|
113
|
+
const lines = buffer.split("\n");
|
|
114
|
+
buffer = lines.pop() || "";
|
|
115
|
+
for (const line of lines) {
|
|
116
|
+
if (line.startsWith("data: ")) {
|
|
117
|
+
const data = line.slice(6).trim();
|
|
118
|
+
if (data) {
|
|
119
|
+
const proxyEvent = JSON.parse(data);
|
|
120
|
+
const event = processProxyEvent(proxyEvent, partial);
|
|
121
|
+
if (event) {
|
|
122
|
+
stream.push(event);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (options.signal?.aborted) {
|
|
129
|
+
throw new Error("Request aborted by user");
|
|
130
|
+
}
|
|
131
|
+
stream.end();
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
135
|
+
const reason = options.signal?.aborted ? "aborted" : "error";
|
|
136
|
+
partial.stopReason = reason;
|
|
137
|
+
partial.errorMessage = errorMessage;
|
|
138
|
+
stream.push({
|
|
139
|
+
type: "error",
|
|
140
|
+
reason,
|
|
141
|
+
error: partial,
|
|
142
|
+
});
|
|
143
|
+
stream.end();
|
|
144
|
+
}
|
|
145
|
+
finally {
|
|
146
|
+
if (options.signal) {
|
|
147
|
+
options.signal.removeEventListener("abort", abortHandler);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
})();
|
|
151
|
+
return stream;
|
|
152
|
+
}
|
|
153
|
+
function processProxyEvent(proxyEvent, partial) {
|
|
154
|
+
switch (proxyEvent.type) {
|
|
155
|
+
case "start":
|
|
156
|
+
return { type: "start", partial };
|
|
157
|
+
case "text_start":
|
|
158
|
+
partial.content[proxyEvent.contentIndex] = { type: "text", text: "" };
|
|
159
|
+
return { type: "text_start", contentIndex: proxyEvent.contentIndex, partial };
|
|
160
|
+
case "text_delta": {
|
|
161
|
+
const content = partial.content[proxyEvent.contentIndex];
|
|
162
|
+
if (content?.type === "text") {
|
|
163
|
+
content.text += proxyEvent.delta;
|
|
164
|
+
return {
|
|
165
|
+
type: "text_delta",
|
|
166
|
+
contentIndex: proxyEvent.contentIndex,
|
|
167
|
+
delta: proxyEvent.delta,
|
|
168
|
+
partial,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
throw new Error("Received text_delta for non-text content");
|
|
172
|
+
}
|
|
173
|
+
case "text_end": {
|
|
174
|
+
const content = partial.content[proxyEvent.contentIndex];
|
|
175
|
+
if (content?.type === "text") {
|
|
176
|
+
content.textSignature = proxyEvent.contentSignature;
|
|
177
|
+
return {
|
|
178
|
+
type: "text_end",
|
|
179
|
+
contentIndex: proxyEvent.contentIndex,
|
|
180
|
+
content: content.text,
|
|
181
|
+
partial,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
throw new Error("Received text_end for non-text content");
|
|
185
|
+
}
|
|
186
|
+
case "thinking_start":
|
|
187
|
+
partial.content[proxyEvent.contentIndex] = { type: "thinking", thinking: "" };
|
|
188
|
+
return { type: "thinking_start", contentIndex: proxyEvent.contentIndex, partial };
|
|
189
|
+
case "thinking_delta": {
|
|
190
|
+
const content = partial.content[proxyEvent.contentIndex];
|
|
191
|
+
if (content?.type === "thinking") {
|
|
192
|
+
content.thinking += proxyEvent.delta;
|
|
193
|
+
return {
|
|
194
|
+
type: "thinking_delta",
|
|
195
|
+
contentIndex: proxyEvent.contentIndex,
|
|
196
|
+
delta: proxyEvent.delta,
|
|
197
|
+
partial,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
throw new Error("Received thinking_delta for non-thinking content");
|
|
201
|
+
}
|
|
202
|
+
case "thinking_end": {
|
|
203
|
+
const content = partial.content[proxyEvent.contentIndex];
|
|
204
|
+
if (content?.type === "thinking") {
|
|
205
|
+
content.thinkingSignature = proxyEvent.contentSignature;
|
|
206
|
+
return {
|
|
207
|
+
type: "thinking_end",
|
|
208
|
+
contentIndex: proxyEvent.contentIndex,
|
|
209
|
+
content: content.thinking,
|
|
210
|
+
partial,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
throw new Error("Received thinking_end for non-thinking content");
|
|
214
|
+
}
|
|
215
|
+
case "toolcall_start":
|
|
216
|
+
partial.content[proxyEvent.contentIndex] = {
|
|
217
|
+
type: "toolCall",
|
|
218
|
+
id: proxyEvent.id,
|
|
219
|
+
name: proxyEvent.toolName,
|
|
220
|
+
arguments: {},
|
|
221
|
+
partialJson: "",
|
|
222
|
+
};
|
|
223
|
+
return { type: "toolcall_start", contentIndex: proxyEvent.contentIndex, partial };
|
|
224
|
+
case "toolcall_delta": {
|
|
225
|
+
const content = partial.content[proxyEvent.contentIndex];
|
|
226
|
+
if (content?.type === "toolCall") {
|
|
227
|
+
content.partialJson += proxyEvent.delta;
|
|
228
|
+
content.arguments = parseStreamingJson(content.partialJson) || {};
|
|
229
|
+
partial.content[proxyEvent.contentIndex] = { ...content }; // Trigger reactivity
|
|
230
|
+
return {
|
|
231
|
+
type: "toolcall_delta",
|
|
232
|
+
contentIndex: proxyEvent.contentIndex,
|
|
233
|
+
delta: proxyEvent.delta,
|
|
234
|
+
partial,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
throw new Error("Received toolcall_delta for non-toolCall content");
|
|
238
|
+
}
|
|
239
|
+
case "toolcall_end": {
|
|
240
|
+
const content = partial.content[proxyEvent.contentIndex];
|
|
241
|
+
if (content?.type === "toolCall") {
|
|
242
|
+
delete content.partialJson;
|
|
243
|
+
return {
|
|
244
|
+
type: "toolcall_end",
|
|
245
|
+
contentIndex: proxyEvent.contentIndex,
|
|
246
|
+
toolCall: content,
|
|
247
|
+
partial,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
return undefined;
|
|
251
|
+
}
|
|
252
|
+
case "done":
|
|
253
|
+
partial.stopReason = proxyEvent.reason;
|
|
254
|
+
partial.usage = proxyEvent.usage;
|
|
255
|
+
return { type: "done", reason: proxyEvent.reason, message: partial };
|
|
256
|
+
case "error":
|
|
257
|
+
partial.stopReason = proxyEvent.reason;
|
|
258
|
+
partial.errorMessage = proxyEvent.errorMessage;
|
|
259
|
+
partial.usage = proxyEvent.usage;
|
|
260
|
+
return { type: "error", reason: proxyEvent.reason, error: partial };
|
|
261
|
+
default: {
|
|
262
|
+
const _exhaustiveCheck = proxyEvent;
|
|
263
|
+
console.warn(`Unhandled proxy event type: ${proxyEvent.type}`);
|
|
264
|
+
return undefined;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
//# sourceMappingURL=proxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy.js","sourceRoot":"","sources":["../src/proxy.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype AssistantMessage,\n\ttype AssistantMessageEvent,\n\ttype Context,\n\tEventStream,\n\ttype Model,\n\tparseStreamingJson,\n\ttype SimpleStreamOptions,\n\ttype StopReason,\n\ttype ToolCall,\n} from \"@ponythewhite/base-context-ai\";\n\nclass ProxyMessageEventStream extends EventStream<AssistantMessageEvent, AssistantMessage> {\n\tconstructor() {\n\t\tsuper(\n\t\t\t(event) => event.type === \"done\" || event.type === \"error\",\n\t\t\t(event) => {\n\t\t\t\tif (event.type === \"done\") return event.message;\n\t\t\t\tif (event.type === \"error\") return event.error;\n\t\t\t\tthrow new Error(\"Unexpected event type\");\n\t\t\t},\n\t\t);\n\t}\n}\n\nexport type ProxyAssistantMessageEvent =\n\t| { type: \"start\" }\n\t| { type: \"text_start\"; contentIndex: number }\n\t| { type: \"text_delta\"; contentIndex: number; delta: string }\n\t| { type: \"text_end\"; contentIndex: number; contentSignature?: string }\n\t| { type: \"thinking_start\"; contentIndex: number }\n\t| { type: \"thinking_delta\"; contentIndex: number; delta: string }\n\t| { type: \"thinking_end\"; contentIndex: number; contentSignature?: string }\n\t| { type: \"toolcall_start\"; contentIndex: number; id: string; toolName: string }\n\t| { type: \"toolcall_delta\"; contentIndex: number; delta: string }\n\t| { type: \"toolcall_end\"; contentIndex: number }\n\t| {\n\t\t\ttype: \"done\";\n\t\t\treason: Extract<StopReason, \"stop\" | \"length\" | \"toolUse\">;\n\t\t\tusage: AssistantMessage[\"usage\"];\n\t }\n\t| {\n\t\t\ttype: \"error\";\n\t\t\treason: Extract<StopReason, \"aborted\" | \"error\">;\n\t\t\terrorMessage?: string;\n\t\t\tusage: AssistantMessage[\"usage\"];\n\t };\n\ntype ProxySerializableStreamOptions = Pick<\n\tSimpleStreamOptions,\n\t| \"temperature\"\n\t| \"maxTokens\"\n\t| \"reasoning\"\n\t| \"cacheRetention\"\n\t| \"sessionId\"\n\t| \"headers\"\n\t| \"metadata\"\n\t| \"transport\"\n\t| \"thinkingBudgets\"\n\t| \"maxRetryDelayMs\"\n>;\n\nexport interface ProxyStreamOptions extends ProxySerializableStreamOptions {\n\tsignal?: AbortSignal;\n\tauthToken: string;\n\tproxyUrl: string;\n}\n\n/**\n * Stream function that proxies through a server instead of calling LLM providers directly.\n * The server strips the partial field from delta events to reduce bandwidth.\n * We reconstruct the partial message client-side.\n *\n * Use this as the `streamFn` option when creating an Agent that needs to go through a proxy.\n *\n * @example\n * ```typescript\n * const agent = new Agent({\n * streamFn: (model, context, options) =>\n * streamProxy(model, context, {\n * ...options,\n * authToken: await getAuthToken(),\n * proxyUrl: \"https://genai.example.com\",\n * }),\n * });\n * ```\n */\nfunction buildProxyRequestOptions(options: ProxyStreamOptions): ProxySerializableStreamOptions {\n\treturn {\n\t\ttemperature: options.temperature,\n\t\tmaxTokens: options.maxTokens,\n\t\treasoning: options.reasoning,\n\t\tcacheRetention: options.cacheRetention,\n\t\tsessionId: options.sessionId,\n\t\theaders: options.headers,\n\t\tmetadata: options.metadata,\n\t\ttransport: options.transport,\n\t\tthinkingBudgets: options.thinkingBudgets,\n\t\tmaxRetryDelayMs: options.maxRetryDelayMs,\n\t};\n}\n\nexport function streamProxy(model: Model<any>, context: Context, options: ProxyStreamOptions): ProxyMessageEventStream {\n\tconst stream = new ProxyMessageEventStream();\n\n\t(async () => {\n\t\tconst partial: AssistantMessage = {\n\t\t\trole: \"assistant\",\n\t\t\tstopReason: \"stop\",\n\t\t\tcontent: [],\n\t\t\tapi: model.api,\n\t\t\tprovider: model.provider,\n\t\t\tmodel: model.id,\n\t\t\tusage: {\n\t\t\t\tinput: 0,\n\t\t\t\toutput: 0,\n\t\t\t\tcacheRead: 0,\n\t\t\t\tcacheWrite: 0,\n\t\t\t\ttotalTokens: 0,\n\t\t\t\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n\t\t\t},\n\t\t\ttimestamp: Date.now(),\n\t\t};\n\n\t\tlet reader: ReadableStreamDefaultReader<Uint8Array> | undefined;\n\n\t\tconst abortHandler = () => {\n\t\t\tif (reader) {\n\t\t\t\treader.cancel(\"Request aborted by user\").catch(() => {});\n\t\t\t}\n\t\t};\n\n\t\tif (options.signal) {\n\t\t\toptions.signal.addEventListener(\"abort\", abortHandler);\n\t\t}\n\n\t\ttry {\n\t\t\tconst response = await fetch(`${options.proxyUrl}/api/stream`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\tAuthorization: `Bearer ${options.authToken}`,\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tmodel,\n\t\t\t\t\tcontext,\n\t\t\t\t\toptions: buildProxyRequestOptions(options),\n\t\t\t\t}),\n\t\t\t\tsignal: options.signal,\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tlet errorMessage = `Proxy error: ${response.status} ${response.statusText}`;\n\t\t\t\ttry {\n\t\t\t\t\tconst errorData = (await response.json()) as { error?: string };\n\t\t\t\t\tif (errorData.error) {\n\t\t\t\t\t\terrorMessage = `Proxy error: ${errorData.error}`;\n\t\t\t\t\t}\n\t\t\t\t} catch {\n\t\t\t\t\t// Keep the status-text fallback when the error body is not JSON.\n\t\t\t\t}\n\t\t\t\tthrow new Error(errorMessage);\n\t\t\t}\n\n\t\t\treader = response.body!.getReader();\n\t\t\tconst decoder = new TextDecoder();\n\t\t\tlet buffer = \"\";\n\n\t\t\twhile (true) {\n\t\t\t\tconst { done, value } = await reader.read();\n\t\t\t\tif (done) break;\n\n\t\t\t\tif (options.signal?.aborted) {\n\t\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t\t}\n\n\t\t\t\tbuffer += decoder.decode(value, { stream: true });\n\t\t\t\tconst lines = buffer.split(\"\\n\");\n\t\t\t\tbuffer = lines.pop() || \"\";\n\n\t\t\t\tfor (const line of lines) {\n\t\t\t\t\tif (line.startsWith(\"data: \")) {\n\t\t\t\t\t\tconst data = line.slice(6).trim();\n\t\t\t\t\t\tif (data) {\n\t\t\t\t\t\t\tconst proxyEvent = JSON.parse(data) as ProxyAssistantMessageEvent;\n\t\t\t\t\t\t\tconst event = processProxyEvent(proxyEvent, partial);\n\t\t\t\t\t\t\tif (event) {\n\t\t\t\t\t\t\t\tstream.push(event);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (options.signal?.aborted) {\n\t\t\t\tthrow new Error(\"Request aborted by user\");\n\t\t\t}\n\n\t\t\tstream.end();\n\t\t} catch (error) {\n\t\t\tconst errorMessage = error instanceof Error ? error.message : String(error);\n\t\t\tconst reason = options.signal?.aborted ? \"aborted\" : \"error\";\n\t\t\tpartial.stopReason = reason;\n\t\t\tpartial.errorMessage = errorMessage;\n\t\t\tstream.push({\n\t\t\t\ttype: \"error\",\n\t\t\t\treason,\n\t\t\t\terror: partial,\n\t\t\t});\n\t\t\tstream.end();\n\t\t} finally {\n\t\t\tif (options.signal) {\n\t\t\t\toptions.signal.removeEventListener(\"abort\", abortHandler);\n\t\t\t}\n\t\t}\n\t})();\n\n\treturn stream;\n}\n\nfunction processProxyEvent(\n\tproxyEvent: ProxyAssistantMessageEvent,\n\tpartial: AssistantMessage,\n): AssistantMessageEvent | undefined {\n\tswitch (proxyEvent.type) {\n\t\tcase \"start\":\n\t\t\treturn { type: \"start\", partial };\n\n\t\tcase \"text_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = { type: \"text\", text: \"\" };\n\t\t\treturn { type: \"text_start\", contentIndex: proxyEvent.contentIndex, partial };\n\n\t\tcase \"text_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"text\") {\n\t\t\t\tcontent.text += proxyEvent.delta;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"text_delta\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received text_delta for non-text content\");\n\t\t}\n\n\t\tcase \"text_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"text\") {\n\t\t\t\tcontent.textSignature = proxyEvent.contentSignature;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"text_end\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tcontent: content.text,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received text_end for non-text content\");\n\t\t}\n\n\t\tcase \"thinking_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = { type: \"thinking\", thinking: \"\" };\n\t\t\treturn { type: \"thinking_start\", contentIndex: proxyEvent.contentIndex, partial };\n\n\t\tcase \"thinking_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\tcontent.thinking += proxyEvent.delta;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"thinking_delta\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received thinking_delta for non-thinking content\");\n\t\t}\n\n\t\tcase \"thinking_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"thinking\") {\n\t\t\t\tcontent.thinkingSignature = proxyEvent.contentSignature;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"thinking_end\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tcontent: content.thinking,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received thinking_end for non-thinking content\");\n\t\t}\n\n\t\tcase \"toolcall_start\":\n\t\t\tpartial.content[proxyEvent.contentIndex] = {\n\t\t\t\ttype: \"toolCall\",\n\t\t\t\tid: proxyEvent.id,\n\t\t\t\tname: proxyEvent.toolName,\n\t\t\t\targuments: {},\n\t\t\t\tpartialJson: \"\",\n\t\t\t} satisfies ToolCall & { partialJson: string } as ToolCall;\n\t\t\treturn { type: \"toolcall_start\", contentIndex: proxyEvent.contentIndex, partial };\n\n\t\tcase \"toolcall_delta\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\t(content as any).partialJson += proxyEvent.delta;\n\t\t\t\tcontent.arguments = parseStreamingJson((content as any).partialJson) || {};\n\t\t\t\tpartial.content[proxyEvent.contentIndex] = { ...content }; // Trigger reactivity\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"toolcall_delta\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\tdelta: proxyEvent.delta,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow new Error(\"Received toolcall_delta for non-toolCall content\");\n\t\t}\n\n\t\tcase \"toolcall_end\": {\n\t\t\tconst content = partial.content[proxyEvent.contentIndex];\n\t\t\tif (content?.type === \"toolCall\") {\n\t\t\t\tdelete (content as any).partialJson;\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"toolcall_end\",\n\t\t\t\t\tcontentIndex: proxyEvent.contentIndex,\n\t\t\t\t\ttoolCall: content,\n\t\t\t\t\tpartial,\n\t\t\t\t};\n\t\t\t}\n\t\t\treturn undefined;\n\t\t}\n\n\t\tcase \"done\":\n\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\treturn { type: \"done\", reason: proxyEvent.reason, message: partial };\n\n\t\tcase \"error\":\n\t\t\tpartial.stopReason = proxyEvent.reason;\n\t\t\tpartial.errorMessage = proxyEvent.errorMessage;\n\t\t\tpartial.usage = proxyEvent.usage;\n\t\t\treturn { type: \"error\", reason: proxyEvent.reason, error: partial };\n\n\t\tdefault: {\n\t\t\tconst _exhaustiveCheck: never = proxyEvent;\n\t\t\tconsole.warn(`Unhandled proxy event type: ${(proxyEvent as any).type}`);\n\t\t\treturn undefined;\n\t\t}\n\t}\n}\n"]}
|