@benjamolina/pi-antigravity-guard 0.3.0 → 0.4.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/README.md +24 -10
- package/dist/catalog.d.ts +57 -1
- package/dist/catalog.js +44 -22
- package/dist/context.d.ts +11 -1
- package/dist/context.js +84 -31
- package/dist/response.d.ts +31 -6
- package/dist/response.js +98 -49
- package/dist/stream.d.ts +21 -4
- package/dist/stream.js +83 -14
- package/dist/tool-context.d.ts +14 -0
- package/dist/tool-context.js +162 -0
- package/dist/tool-contract.d.ts +17 -0
- package/dist/tool-contract.js +65 -0
- package/dist/tool-schema.d.ts +7 -0
- package/dist/tool-schema.js +125 -0
- package/package.json +1 -1
package/dist/response.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import type { JsonObject } from "./tool-contract.ts";
|
|
2
|
+
export type ResponseSemantic = TextSemantic | ThinkingSemantic | ToolCallSemantic | FinishSemantic | UsageSemantic;
|
|
2
3
|
export interface TextSemantic {
|
|
3
4
|
type: "text";
|
|
4
5
|
text: string;
|
|
@@ -9,9 +10,18 @@ export interface ThinkingSemantic {
|
|
|
9
10
|
thinking: string;
|
|
10
11
|
signature?: string;
|
|
11
12
|
}
|
|
13
|
+
export interface ToolCallSemantic {
|
|
14
|
+
type: "toolCall";
|
|
15
|
+
callIndex: number;
|
|
16
|
+
id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
arguments: JsonObject;
|
|
19
|
+
argumentsJson: string;
|
|
20
|
+
signature?: string;
|
|
21
|
+
}
|
|
12
22
|
export interface FinishSemantic {
|
|
13
23
|
type: "finish";
|
|
14
|
-
reason: "stop" | "length";
|
|
24
|
+
reason: "stop" | "length" | "toolUse";
|
|
15
25
|
}
|
|
16
26
|
export interface UsageSemantic {
|
|
17
27
|
type: "usage";
|
|
@@ -22,16 +32,31 @@ export interface UsageSemantic {
|
|
|
22
32
|
reasoning: number;
|
|
23
33
|
total: number;
|
|
24
34
|
}
|
|
35
|
+
export type ToolResponsePolicy = {
|
|
36
|
+
kind: "reject";
|
|
37
|
+
} | {
|
|
38
|
+
kind: "accept";
|
|
39
|
+
declaredNames: ReadonlySet<string>;
|
|
40
|
+
};
|
|
25
41
|
export declare class ResponseSemanticError extends Error {
|
|
26
42
|
}
|
|
27
43
|
export declare class ResponseSemantics {
|
|
44
|
+
readonly policy: ToolResponsePolicy;
|
|
28
45
|
private finished;
|
|
29
|
-
private content;
|
|
30
46
|
private done;
|
|
47
|
+
private content;
|
|
48
|
+
private calls;
|
|
49
|
+
private readonly ids;
|
|
50
|
+
private pendingFinish;
|
|
31
51
|
private usage;
|
|
52
|
+
constructor(policy?: ToolResponsePolicy);
|
|
32
53
|
push(record: string): ResponseSemantic[];
|
|
33
|
-
finish():
|
|
34
|
-
addContent(text: string): void;
|
|
35
|
-
addFinish(): void;
|
|
54
|
+
finish(): FinishSemantic;
|
|
36
55
|
private updateUsage;
|
|
56
|
+
addContent(text: string): void;
|
|
57
|
+
addCall(): void;
|
|
58
|
+
setFinish(reason: FinishSemantic["reason"]): void;
|
|
59
|
+
hasId(id: string): boolean;
|
|
60
|
+
addId(id: string): void;
|
|
61
|
+
callIndex(): number;
|
|
37
62
|
}
|
package/dist/response.js
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
|
+
import { canonicalJson } from "./tool-contract.js";
|
|
1
2
|
export class ResponseSemanticError extends Error {
|
|
2
3
|
}
|
|
3
4
|
export class ResponseSemantics {
|
|
5
|
+
policy;
|
|
4
6
|
finished = false;
|
|
5
|
-
content = false;
|
|
6
7
|
done = false;
|
|
8
|
+
content = false;
|
|
9
|
+
calls = 0;
|
|
10
|
+
ids = new Set();
|
|
11
|
+
pendingFinish;
|
|
7
12
|
usage = { prompt: 0, cacheRead: 0, output: 0, reasoning: 0 };
|
|
13
|
+
constructor(policy = { kind: "reject" }) {
|
|
14
|
+
this.policy = policy;
|
|
15
|
+
}
|
|
8
16
|
push(record) {
|
|
9
17
|
if (this.done)
|
|
10
18
|
throw new ResponseSemanticError("Response data arrived after [DONE].");
|
|
@@ -37,9 +45,8 @@ export class ResponseSemantics {
|
|
|
37
45
|
throw new ResponseSemanticError("Response ended without a finish reason.");
|
|
38
46
|
if (!this.content)
|
|
39
47
|
throw new ResponseSemanticError("Response ended without content.");
|
|
48
|
+
return { type: "finish", reason: this.pendingFinish };
|
|
40
49
|
}
|
|
41
|
-
addContent(text) { this.content = this.content || text.length > 0; }
|
|
42
|
-
addFinish() { this.finished = true; }
|
|
43
50
|
updateUsage(value) {
|
|
44
51
|
const prompt = count(value, "promptTokenCount", this.usage.prompt);
|
|
45
52
|
const cacheRead = count(value, "cachedContentTokenCount", this.usage.cacheRead);
|
|
@@ -52,74 +59,116 @@ export class ResponseSemantics {
|
|
|
52
59
|
this.usage = { prompt, cacheRead, output, reasoning };
|
|
53
60
|
return { type: "usage", input: prompt - cacheRead, output, cacheRead, cacheWrite: 0, reasoning, total: prompt + output };
|
|
54
61
|
}
|
|
62
|
+
addContent(text) { this.content = this.content || text.length > 0; }
|
|
63
|
+
addCall() { this.content = true; this.calls++; }
|
|
64
|
+
setFinish(reason) { this.finished = true; this.pendingFinish = reason; }
|
|
65
|
+
hasId(id) { return this.ids.has(id); }
|
|
66
|
+
addId(id) { this.ids.add(id); }
|
|
67
|
+
callIndex() { return this.calls; }
|
|
55
68
|
}
|
|
56
69
|
function candidate(value, events, semantics) {
|
|
57
70
|
if (!Array.isArray(value) || value.length !== 1)
|
|
58
71
|
throw new ResponseSemanticError("Antigravity returned invalid candidates.");
|
|
59
|
-
const
|
|
60
|
-
if (
|
|
72
|
+
const item = value[0];
|
|
73
|
+
if (item === undefined)
|
|
61
74
|
return;
|
|
62
|
-
const index = field(
|
|
75
|
+
const index = field(item, "index");
|
|
63
76
|
if (index !== undefined && index !== 0)
|
|
64
77
|
throw new ResponseSemanticError("Antigravity returned invalid candidates.");
|
|
65
|
-
const content = field(
|
|
78
|
+
const content = field(item, "content");
|
|
79
|
+
const local = [];
|
|
80
|
+
const ids = [];
|
|
81
|
+
let callCount = 0;
|
|
66
82
|
if (content !== undefined) {
|
|
67
83
|
const parts = field(content, "parts");
|
|
68
84
|
if (!Array.isArray(parts))
|
|
69
85
|
throw new ResponseSemanticError("Antigravity returned invalid content.");
|
|
70
86
|
for (const part of parts) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
87
|
+
if (field(part, "functionCall") !== undefined) {
|
|
88
|
+
const call = toolCall(part, semantics, ids, callCount++);
|
|
89
|
+
ids.push(call.id);
|
|
90
|
+
local.push(call);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
const text = field(part, "text");
|
|
94
|
+
const thought = field(part, "thought");
|
|
95
|
+
if (typeof text !== "string" || (thought !== undefined && typeof thought !== "boolean") || keys(part, ["text", "thought", "thoughtSignature"]))
|
|
96
|
+
throw new ResponseSemanticError("Antigravity returned unsupported content.");
|
|
97
|
+
const signature = validThoughtSignature(field(part, "thoughtSignature"));
|
|
98
|
+
local.push(thought === true ? { type: "thinking", thinking: text, ...(signature ? { signature } : {}) } : { type: "text", text, ...(signature ? { signature } : {}) });
|
|
99
|
+
}
|
|
81
100
|
}
|
|
82
101
|
}
|
|
83
|
-
const finishReason = field(
|
|
102
|
+
const finishReason = field(item, "finishReason");
|
|
84
103
|
if (finishReason !== undefined) {
|
|
85
|
-
|
|
104
|
+
const hasCalls = semantics.callIndex() + callCount > 0;
|
|
105
|
+
const reason = finishReason === "STOP" ? "stop" : finishReason === "MAX_TOKENS" ? "length" : finishReason === "OTHER" && hasCalls ? "toolUse" : undefined;
|
|
106
|
+
if (!reason || (hasCalls && reason !== "toolUse"))
|
|
86
107
|
throw new ResponseSemanticError("Antigravity returned an unsupported finish reason.");
|
|
87
|
-
semantics.
|
|
88
|
-
events.push({ type: "finish", reason: finishReason === "STOP" ? "stop" : "length" });
|
|
108
|
+
semantics.setFinish(reason);
|
|
89
109
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
throw new ResponseSemanticError("Antigravity returned invalid metadata.");
|
|
110
|
+
for (const id of ids)
|
|
111
|
+
semantics.addId(id);
|
|
112
|
+
for (const event of local) {
|
|
113
|
+
if (event.type === "text")
|
|
114
|
+
semantics.addContent(event.text);
|
|
115
|
+
else if (event.type === "thinking")
|
|
116
|
+
semantics.addContent(event.thinking);
|
|
117
|
+
else
|
|
118
|
+
semantics.addCall();
|
|
119
|
+
events.push(event);
|
|
101
120
|
}
|
|
102
121
|
}
|
|
103
|
-
function
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if (
|
|
108
|
-
throw new ResponseSemanticError("Antigravity returned
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
122
|
+
function toolCall(part, semantics, ids, offset) {
|
|
123
|
+
if (semantics.policy.kind !== "accept" || keys(part, ["functionCall", "thoughtSignature"]))
|
|
124
|
+
throw new ResponseSemanticError("Antigravity returned unsupported content.");
|
|
125
|
+
const call = field(part, "functionCall");
|
|
126
|
+
if (keys(call, ["id", "name", "args"]))
|
|
127
|
+
throw new ResponseSemanticError("Antigravity returned unsupported content.");
|
|
128
|
+
const id = field(call, "id");
|
|
129
|
+
const name = field(call, "name");
|
|
130
|
+
const args = field(call, "args");
|
|
131
|
+
if (typeof id !== "string" || !id.trim() || typeof name !== "string" || !semantics.policy.declaredNames.has(name) || semantics.hasId(id) || ids.includes(id))
|
|
132
|
+
throw new ResponseSemanticError("Antigravity returned invalid function call.");
|
|
133
|
+
let argumentsValue;
|
|
112
134
|
try {
|
|
113
|
-
|
|
135
|
+
const canonical = canonicalJson(args, name, "$");
|
|
136
|
+
if (Array.isArray(canonical) || canonical === null)
|
|
137
|
+
throw new Error();
|
|
138
|
+
argumentsValue = canonical;
|
|
114
139
|
}
|
|
115
140
|
catch {
|
|
116
|
-
throw new ResponseSemanticError("Antigravity returned invalid
|
|
141
|
+
throw new ResponseSemanticError("Antigravity returned invalid function call.");
|
|
117
142
|
}
|
|
143
|
+
const argumentsJson = JSON.stringify(argumentsValue);
|
|
144
|
+
if (new TextEncoder().encode(argumentsJson).byteLength > 1024 * 1024 || depth(argumentsValue) > 64)
|
|
145
|
+
throw new ResponseSemanticError("Antigravity returned invalid function call.");
|
|
146
|
+
const signature = validThoughtSignature(field(part, "thoughtSignature"));
|
|
147
|
+
return { type: "toolCall", callIndex: semantics.callIndex() + offset, id, name, arguments: argumentsValue, argumentsJson, ...(signature ? { signature } : {}) };
|
|
118
148
|
}
|
|
119
|
-
function
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
return descriptor && "value" in descriptor ? descriptor.value : undefined;
|
|
149
|
+
function depth(value, level = 0) {
|
|
150
|
+
let maximum = level;
|
|
151
|
+
for (const item of Object.values(value))
|
|
152
|
+
maximum = Math.max(maximum, depthValue(item, level + 1));
|
|
153
|
+
return maximum;
|
|
125
154
|
}
|
|
155
|
+
function depthValue(value, level) { return value && typeof value === "object" ? depth(value, level) : level; }
|
|
156
|
+
function keys(value, allowed) { return typeof value !== "object" || value === null || Array.isArray(value) || Object.keys(value).some((name) => !allowed.includes(name)); }
|
|
157
|
+
function validThoughtSignature(value) { return typeof value === "string" && value && value.length % 4 === 0 && /^[A-Za-z0-9+/]+={0,2}$/.test(value) ? value : undefined; }
|
|
158
|
+
function validateMetadata(value) { for (const name of ["responseId", "modelVersion"]) {
|
|
159
|
+
const metadata = field(value, name);
|
|
160
|
+
if (metadata !== undefined && (typeof metadata !== "string" || !metadata))
|
|
161
|
+
throw new ResponseSemanticError("Antigravity returned invalid metadata.");
|
|
162
|
+
} }
|
|
163
|
+
function count(value, name, fallback) { const item = field(value, name); if (item === undefined)
|
|
164
|
+
return fallback; if (typeof item !== "number" || !Number.isSafeInteger(item) || item < 0)
|
|
165
|
+
throw new ResponseSemanticError("Antigravity returned invalid usage."); return item; }
|
|
166
|
+
function parse(record) { try {
|
|
167
|
+
return JSON.parse(record);
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
throw new ResponseSemanticError("Antigravity returned invalid JSON.");
|
|
171
|
+
} }
|
|
172
|
+
function hasError(value) { return field(value, "error") !== undefined; }
|
|
173
|
+
function field(value, name) { if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
174
|
+
throw new ResponseSemanticError("Antigravity returned an invalid response."); const descriptor = Object.getOwnPropertyDescriptor(value, name); return descriptor && "value" in descriptor ? descriptor.value : undefined; }
|
package/dist/stream.d.ts
CHANGED
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
import type { Context, Model, SimpleStreamOptions } from "@earendil-works/pi-ai";
|
|
2
|
+
import { type GenerationSelection } from "./catalog.ts";
|
|
2
3
|
import type { ResponseSemantic } from "./response.ts";
|
|
3
|
-
type StreamErrorKind = "aborted" | "access" | "model" | "quota" | "response" | "transport" | "callback";
|
|
4
|
+
type StreamErrorKind = "aborted" | "access" | "capability" | "model" | "quota" | "preflight" | "response" | "transport" | "callback";
|
|
5
|
+
export interface ToolStreamDiagnostics {
|
|
6
|
+
readonly publicModelId: string;
|
|
7
|
+
readonly reasoning: string;
|
|
8
|
+
readonly capabilityState: string;
|
|
9
|
+
readonly preflight?: "accepted" | string;
|
|
10
|
+
readonly preflightCategory?: string;
|
|
11
|
+
readonly preflightPath?: string;
|
|
12
|
+
readonly recoveryCount?: number;
|
|
13
|
+
}
|
|
14
|
+
type ToolDiagnosticSemantic = {
|
|
15
|
+
type: "toolDiagnostics";
|
|
16
|
+
details: ToolStreamDiagnostics;
|
|
17
|
+
};
|
|
18
|
+
type StreamSemantic = ResponseSemantic | ToolDiagnosticSemantic;
|
|
4
19
|
export declare class StreamTransportError extends Error {
|
|
5
20
|
readonly kind: StreamErrorKind;
|
|
6
21
|
readonly status?: number | undefined;
|
|
7
|
-
|
|
22
|
+
readonly details?: ToolStreamDiagnostics | undefined;
|
|
23
|
+
constructor(kind: StreamErrorKind, message: string, status?: number | undefined, details?: ToolStreamDiagnostics | undefined);
|
|
8
24
|
}
|
|
9
25
|
export interface StreamTransportInput {
|
|
10
26
|
accessToken: string;
|
|
@@ -16,9 +32,10 @@ export interface StreamTransportInput {
|
|
|
16
32
|
model: Model<string>;
|
|
17
33
|
projectId: string;
|
|
18
34
|
now: () => number;
|
|
19
|
-
onSemantic: (semantic:
|
|
35
|
+
onSemantic: (semantic: StreamSemantic) => void | Promise<void>;
|
|
20
36
|
platform: string;
|
|
21
37
|
requestId: string;
|
|
38
|
+
selection?: GenerationSelection;
|
|
22
39
|
signal?: AbortSignal;
|
|
23
40
|
timeoutMs?: number;
|
|
24
41
|
}
|
|
@@ -27,7 +44,7 @@ export interface PiStreamLifecycleInput {
|
|
|
27
44
|
now: () => number;
|
|
28
45
|
signal?: AbortSignal;
|
|
29
46
|
runTransport: (input: {
|
|
30
|
-
onSemantic: (semantic:
|
|
47
|
+
onSemantic: (semantic: StreamSemantic) => void;
|
|
31
48
|
signal: AbortSignal;
|
|
32
49
|
}) => Promise<void>;
|
|
33
50
|
}
|
package/dist/stream.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { calculateCost, createAssistantMessageEventStream } from "@earendil-works/pi-ai";
|
|
2
2
|
import { ANTIGRAVITY_ENDPOINTS } from "@benjamolina/antigravity-guard-core";
|
|
3
|
-
import { getCatalogEntry } from "./catalog.js";
|
|
4
|
-
import {
|
|
3
|
+
import { getCatalogEntry, resolveGenerationSelection } from "./catalog.js";
|
|
4
|
+
import { ContextSerializationError, serializeContext } from "./context.js";
|
|
5
|
+
import { ToolPreflightError } from "./tool-contract.js";
|
|
6
|
+
import { hasToolContext } from "./tool-context.js";
|
|
5
7
|
import { ResponseSemanticError, ResponseSemantics } from "./response.js";
|
|
6
8
|
import { SseFrameError, SseFramer } from "./sse.js";
|
|
7
9
|
const TOTAL_TIMEOUT_MS = 120_000;
|
|
@@ -15,10 +17,12 @@ const LOCAL_ERRORS = new WeakSet();
|
|
|
15
17
|
export class StreamTransportError extends Error {
|
|
16
18
|
kind;
|
|
17
19
|
status;
|
|
18
|
-
|
|
20
|
+
details;
|
|
21
|
+
constructor(kind, message, status, details) {
|
|
19
22
|
super(message);
|
|
20
23
|
this.kind = kind;
|
|
21
24
|
this.status = status;
|
|
25
|
+
this.details = details;
|
|
22
26
|
}
|
|
23
27
|
}
|
|
24
28
|
export function createPiLifecycleStream(input) {
|
|
@@ -36,6 +40,8 @@ export function createPiLifecycleStream(input) {
|
|
|
36
40
|
timestamp: input.now(),
|
|
37
41
|
};
|
|
38
42
|
let complete = false;
|
|
43
|
+
let sawToolCall = false;
|
|
44
|
+
let toolDiagnostics;
|
|
39
45
|
let textStarted = false;
|
|
40
46
|
let currentBlock;
|
|
41
47
|
const closeBlock = () => {
|
|
@@ -49,15 +55,23 @@ export function createPiLifecycleStream(input) {
|
|
|
49
55
|
currentBlock = undefined;
|
|
50
56
|
};
|
|
51
57
|
let removeAbort = () => { };
|
|
52
|
-
const finalize = (reason, errorMessage) => {
|
|
58
|
+
const finalize = (reason, errorMessage, details) => {
|
|
53
59
|
if (complete)
|
|
54
60
|
return;
|
|
55
61
|
complete = true;
|
|
56
62
|
removeAbort();
|
|
57
|
-
if (reason === "error" || reason === "aborted")
|
|
63
|
+
if (reason === "error" || reason === "aborted") {
|
|
58
64
|
controller.abort();
|
|
65
|
+
for (let index = output.content.length - 1; index >= 0; index--) {
|
|
66
|
+
if (output.content[index]?.type === "toolCall")
|
|
67
|
+
output.content.splice(index, 1);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
59
70
|
output.stopReason = reason;
|
|
60
|
-
|
|
71
|
+
const diagnostic = details ?? toolDiagnostics;
|
|
72
|
+
if (sawToolCall || diagnostic)
|
|
73
|
+
output.diagnostics = [{ type: "antigravity-guard.tools", timestamp: input.now(), details: { ...diagnostic, terminal: reason } }];
|
|
74
|
+
if (reason === "stop" || reason === "length" || reason === "toolUse") {
|
|
61
75
|
closeBlock();
|
|
62
76
|
stream.push({ type: "done", reason, message: output });
|
|
63
77
|
}
|
|
@@ -70,6 +84,21 @@ export function createPiLifecycleStream(input) {
|
|
|
70
84
|
const onSemantic = (semantic) => {
|
|
71
85
|
if (complete)
|
|
72
86
|
return;
|
|
87
|
+
if (semantic.type === "toolDiagnostics") {
|
|
88
|
+
toolDiagnostics = semantic.details;
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (semantic.type === "toolCall") {
|
|
92
|
+
sawToolCall = true;
|
|
93
|
+
closeBlock();
|
|
94
|
+
const toolCall = { type: "toolCall", id: semantic.id, name: semantic.name, arguments: semantic.arguments, ...(semantic.signature ? { thoughtSignature: semantic.signature } : {}) };
|
|
95
|
+
output.content.push(toolCall);
|
|
96
|
+
const contentIndex = output.content.length - 1;
|
|
97
|
+
stream.push({ type: "toolcall_start", contentIndex, partial: output });
|
|
98
|
+
stream.push({ type: "toolcall_delta", contentIndex, delta: semantic.argumentsJson, partial: output });
|
|
99
|
+
stream.push({ type: "toolcall_end", contentIndex, toolCall, partial: output });
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
73
102
|
if (isContentSemantic(semantic)) {
|
|
74
103
|
if (!currentBlock || currentBlock.type !== semantic.type) {
|
|
75
104
|
closeBlock();
|
|
@@ -132,7 +161,7 @@ export function createPiLifecycleStream(input) {
|
|
|
132
161
|
}
|
|
133
162
|
if (!complete)
|
|
134
163
|
void Promise.resolve().then(() => input.runTransport({ onSemantic, signal })).then(() => { if (!complete)
|
|
135
|
-
finalize(signal.aborted ? "aborted" : "error"); }, (error) => finalize(signal.aborted || input.signal?.aborted ? "aborted" : "error", isLocalStreamError(error) ? error.message : undefined));
|
|
164
|
+
finalize(signal.aborted ? "aborted" : "error"); }, (error) => finalize(signal.aborted || input.signal?.aborted ? "aborted" : "error", isLocalStreamError(error) ? error.message : undefined, isLocalStreamError(error) ? error.details : undefined));
|
|
136
165
|
return stream;
|
|
137
166
|
}
|
|
138
167
|
function isContentSemantic(semantic) {
|
|
@@ -143,8 +172,14 @@ export async function executeStreamTransport(input) {
|
|
|
143
172
|
const signal = totalSignal(input);
|
|
144
173
|
const headers = requestHeaders(input);
|
|
145
174
|
let responseBody = null;
|
|
175
|
+
let selection;
|
|
176
|
+
let recoveryCount = 0;
|
|
146
177
|
try {
|
|
147
|
-
const
|
|
178
|
+
const entry = getCatalogEntry(input.model.id);
|
|
179
|
+
selection = input.selection ?? resolveGenerationSelection(entry, input.generationOptions?.reasoning);
|
|
180
|
+
const original = serializeContext({ context: input.context, model: input.model, options: input.generationOptions, project: input.projectId, requestId: input.requestId }, selection, (count) => { recoveryCount = count; });
|
|
181
|
+
if (hasToolContext(input.context))
|
|
182
|
+
await deliver(input.onSemantic, { type: "toolDiagnostics", details: toolDiagnostics(entry.publicId, selection, recoveryCount, "accepted") });
|
|
148
183
|
const payload = await payloadHook(input, original, signal);
|
|
149
184
|
const response = await abortable(input.fetch(ENDPOINT, { method: "POST", redirect: "error", headers, body: JSON.stringify(payload), signal }), signal);
|
|
150
185
|
await responseHook(input, response, signal);
|
|
@@ -155,7 +190,7 @@ export async function executeStreamTransport(input) {
|
|
|
155
190
|
await boundedBody(response.body, signal);
|
|
156
191
|
throw streamError("response", "Antigravity did not return an SSE response.");
|
|
157
192
|
}
|
|
158
|
-
await consume(response.body, signal, input.onSemantic, inactivityTimeout(input));
|
|
193
|
+
await consume(response.body, signal, input.onSemantic, inactivityTimeout(input), responsePolicy(original, selection));
|
|
159
194
|
}
|
|
160
195
|
catch (error) {
|
|
161
196
|
void responseBody?.cancel().catch(() => undefined);
|
|
@@ -163,6 +198,13 @@ export async function executeStreamTransport(input) {
|
|
|
163
198
|
throw error;
|
|
164
199
|
if (signal.aborted)
|
|
165
200
|
throw streamError("aborted", "Generation was cancelled.");
|
|
201
|
+
if (error instanceof ToolPreflightError && selection) {
|
|
202
|
+
const details = toolDiagnostics(getCatalogEntry(input.model.id).publicId, selection, recoveryCount, error.code, error.path);
|
|
203
|
+
throw streamError("preflight", error.message, undefined, details);
|
|
204
|
+
}
|
|
205
|
+
if (error instanceof ContextSerializationError && error.message.startsWith("PI_TOOL_CAPABILITY_NOT_ENABLED") && selection) {
|
|
206
|
+
throw streamError("capability", error.message, undefined, toolDiagnostics(getCatalogEntry(input.model.id).publicId, selection, recoveryCount, "PI_TOOL_CAPABILITY_NOT_ENABLED"));
|
|
207
|
+
}
|
|
166
208
|
if (error instanceof SseFrameError || error instanceof ResponseSemanticError)
|
|
167
209
|
throw streamError("response", "Antigravity returned an invalid stream.");
|
|
168
210
|
throw streamError("transport", "Antigravity generation request failed.");
|
|
@@ -172,6 +214,33 @@ function validateInput(input) {
|
|
|
172
214
|
if (!getCatalogEntry(input.model.id) || input.model.api !== API)
|
|
173
215
|
throw streamError("response", "The selected Antigravity model or API is unsupported.");
|
|
174
216
|
}
|
|
217
|
+
function toolDiagnostics(publicModelId, selection, recoveryCount, preflight, preflightPath) {
|
|
218
|
+
return {
|
|
219
|
+
publicModelId,
|
|
220
|
+
reasoning: selection.level,
|
|
221
|
+
capabilityState: selection.tools.state,
|
|
222
|
+
preflight,
|
|
223
|
+
...(preflight === "accepted" ? { recoveryCount } : { preflightCategory: preflightCategory(preflight), ...(preflightPath ? { preflightPath } : {}) }),
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
function preflightCategory(code) {
|
|
227
|
+
if (code.includes("SCHEMA"))
|
|
228
|
+
return "schema";
|
|
229
|
+
if (code.includes("CHOICE"))
|
|
230
|
+
return "tool-choice";
|
|
231
|
+
if (code.includes("DECLARATION") || code.includes("CONSTRAINED"))
|
|
232
|
+
return "declaration";
|
|
233
|
+
if (code.includes("RESULT") || code.includes("CALL") || code.includes("HISTORY"))
|
|
234
|
+
return "history";
|
|
235
|
+
return "capability";
|
|
236
|
+
}
|
|
237
|
+
function responsePolicy(payload, selection) {
|
|
238
|
+
const declarations = payload.request.tools?.[0]?.functionDeclarations;
|
|
239
|
+
if (selection.tools.state !== "enabled" || payload.request.toolConfig?.functionCallingConfig.mode !== "AUTO" || !Array.isArray(declarations))
|
|
240
|
+
return { kind: "reject" };
|
|
241
|
+
const names = declarations.map((item) => typeof item === "object" && item !== null ? Object.getOwnPropertyDescriptor(item, "name")?.value : undefined);
|
|
242
|
+
return names.every((name) => typeof name === "string") ? { kind: "accept", declaredNames: new Set(names) } : { kind: "reject" };
|
|
243
|
+
}
|
|
175
244
|
async function payloadHook(input, payload, signal) {
|
|
176
245
|
try {
|
|
177
246
|
const replacement = await abortable(Promise.resolve().then(() => input.generationOptions?.onPayload?.(payload, input.model)), signal);
|
|
@@ -209,10 +278,10 @@ function requestHeaders(input) {
|
|
|
209
278
|
"User-Agent": ANTIGRAVITY_USER_AGENT,
|
|
210
279
|
};
|
|
211
280
|
}
|
|
212
|
-
async function consume(body, signal, onSemantic, inactivityMs) {
|
|
281
|
+
async function consume(body, signal, onSemantic, inactivityMs, policy) {
|
|
213
282
|
const reader = body.getReader();
|
|
214
283
|
const framer = new SseFramer();
|
|
215
|
-
const semantics = new ResponseSemantics();
|
|
284
|
+
const semantics = new ResponseSemantics(policy);
|
|
216
285
|
try {
|
|
217
286
|
while (true) {
|
|
218
287
|
const next = await abortable(reader.read(), AbortSignal.any([signal, AbortSignal.timeout(inactivityMs)]));
|
|
@@ -225,7 +294,7 @@ async function consume(body, signal, onSemantic, inactivityMs) {
|
|
|
225
294
|
for (const record of framer.finish())
|
|
226
295
|
for (const semantic of semantics.push(record))
|
|
227
296
|
await deliver(onSemantic, semantic);
|
|
228
|
-
semantics.finish();
|
|
297
|
+
await deliver(onSemantic, semantics.finish());
|
|
229
298
|
}
|
|
230
299
|
catch (error) {
|
|
231
300
|
void reader.cancel().catch(() => undefined);
|
|
@@ -295,8 +364,8 @@ function isSse(value) { return value?.split(";", 1)[0]?.trim().toLowerCase() ===
|
|
|
295
364
|
function inactivityTimeout(input) {
|
|
296
365
|
return input.inactivityTimeoutMs === undefined ? INACTIVITY_TIMEOUT_MS : input.inactivityTimeoutMs > 0 && Number.isFinite(input.inactivityTimeoutMs) ? Math.min(input.inactivityTimeoutMs, INACTIVITY_TIMEOUT_MS) : 0;
|
|
297
366
|
}
|
|
298
|
-
function streamError(kind, message, status) {
|
|
299
|
-
const error = new StreamTransportError(kind, message, status);
|
|
367
|
+
function streamError(kind, message, status, details) {
|
|
368
|
+
const error = new StreamTransportError(kind, message, status, details);
|
|
300
369
|
LOCAL_ERRORS.add(error);
|
|
301
370
|
return error;
|
|
302
371
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type ToolDeclaration } from "./tool-schema.ts";
|
|
2
|
+
export interface PreparedToolContext {
|
|
3
|
+
readonly declarations: readonly ToolDeclaration[];
|
|
4
|
+
readonly mode: "AUTO" | "NONE";
|
|
5
|
+
}
|
|
6
|
+
export declare function hasToolContext(context: unknown): boolean;
|
|
7
|
+
export declare function prepareToolContext(tools: unknown, choice: unknown): PreparedToolContext | undefined;
|
|
8
|
+
type WirePart = object;
|
|
9
|
+
type WireContent = {
|
|
10
|
+
role: "user" | "model";
|
|
11
|
+
parts: WirePart[];
|
|
12
|
+
};
|
|
13
|
+
export declare function replayToolHistory(messages: unknown[], serializePart: (part: Record<string, unknown>, message: Record<string, unknown>) => WirePart, onRecovery?: (recoveryCount: number) => void): WireContent[];
|
|
14
|
+
export {};
|