@benjamolina/pi-antigravity-guard 0.1.0 → 0.2.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 +5 -3
- package/dist/context.d.ts +9 -4
- package/dist/context.js +60 -16
- package/dist/provider.js +2 -1
- package/dist/response.d.ts +10 -3
- package/dist/response.js +23 -11
- package/dist/stream.js +45 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Pi Antigravity Guard
|
|
2
2
|
|
|
3
|
-
`@benjamolina/pi-antigravity-guard` is a Pi extension that registers the text-only `antigravity-guard` provider. It exposes exactly one selectable model, `antigravity-gemini-3.8-flash`, which is sent to Antigravity as `gemini-3.8-flash`.
|
|
3
|
+
`@benjamolina/pi-antigravity-guard` is a Pi extension that registers the text-only `antigravity-guard` provider. It exposes exactly one selectable model, `antigravity-gemini-3.8-flash`, which is sent to Antigravity as `gemini-3.8-flash-tiered`.
|
|
4
4
|
|
|
5
5
|
## Use
|
|
6
6
|
|
|
@@ -12,13 +12,15 @@ Install the package in Pi, then run:
|
|
|
12
12
|
|
|
13
13
|
Choose browser login, or choose manual login and paste the complete callback URL. The loopback callback uses fixed port `51121`; browser callback failures, remote shells, and port conflicts can use the manual callback-URL flow.
|
|
14
14
|
|
|
15
|
-
Start a fresh text-only session with `--no-builtin-tools` and disable any active extension-provided tools. That flag does not disable extension tools by itself. Tools, tool history,
|
|
15
|
+
Start a fresh text-only session with `--no-builtin-tools` and disable any active extension-provided tools. That flag does not disable extension tools by itself. Tools, tool history, and images are unsupported and rejected rather than silently changed.
|
|
16
|
+
|
|
17
|
+
`@benjamolina/pi-antigravity-guard@0.2.0` supports Pi reasoning levels `low`, `medium`, and `high`. An authorized live matrix against `gemini-3.8-flash-tiered` confirmed all three levels; `minimal` was rejected twice with zero usage and is not exposed. Each supported level is sent as the matching native `thinkingLevel` with visible thoughts enabled. When reasoning is off or omitted, the request uses native `low` thinking without returning visible thoughts. Custom `thinkingBudgets` are unsupported and rejected.
|
|
16
18
|
|
|
17
19
|
## Limits and safety
|
|
18
20
|
|
|
19
21
|
- This provider stores credentials through Pi's OAuth lifecycle and never reads or changes OpenCode account files; each is independent.
|
|
20
22
|
- Costs are reported as zero because subscription usage is unpriced, not because access is free.
|
|
21
|
-
- The
|
|
23
|
+
- The public-to-wire model mapping remains separate from availability and entitlement. The documented reasoning levels are limited to the authorized live matrix results above.
|
|
22
24
|
- It has no account rotation, quota fallback, model substitution, or tool support.
|
|
23
25
|
|
|
24
26
|
To remove it, disable or uninstall `@benjamolina/pi-antigravity-guard` and reload Pi. Removal does not delete Pi-managed credentials or revoke tokens.
|
package/dist/context.d.ts
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import type { Context, Model, SimpleStreamOptions } from "@earendil-works/pi-ai";
|
|
2
2
|
declare const WIRE_MODEL = "gemini-3.8-flash-tiered";
|
|
3
|
+
declare const THINKING_LEVELS: readonly ["low", "medium", "high"];
|
|
4
|
+
type ThinkingLevel = typeof THINKING_LEVELS[number];
|
|
3
5
|
interface Part {
|
|
4
6
|
text: string;
|
|
7
|
+
thought?: true;
|
|
8
|
+
thoughtSignature?: string;
|
|
5
9
|
}
|
|
6
10
|
interface Content {
|
|
7
11
|
role: "user" | "model";
|
|
8
12
|
parts: Part[];
|
|
9
13
|
}
|
|
14
|
+
interface ThinkingConfig {
|
|
15
|
+
thinkingLevel: ThinkingLevel;
|
|
16
|
+
includeThoughts: boolean;
|
|
17
|
+
}
|
|
10
18
|
export interface GenerationRequest {
|
|
11
19
|
project: string;
|
|
12
20
|
model: typeof WIRE_MODEL;
|
|
@@ -18,10 +26,7 @@ export interface GenerationRequest {
|
|
|
18
26
|
generationConfig: {
|
|
19
27
|
temperature: number;
|
|
20
28
|
maxOutputTokens: number;
|
|
21
|
-
thinkingConfig:
|
|
22
|
-
thinkingLevel: "low";
|
|
23
|
-
includeThoughts: false;
|
|
24
|
-
};
|
|
29
|
+
thinkingConfig: ThinkingConfig;
|
|
25
30
|
};
|
|
26
31
|
};
|
|
27
32
|
requestType: "agent";
|
package/dist/context.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
const MAX_TEXT_BYTES = 8 * 1024 * 1024;
|
|
2
2
|
const MAX_OUTPUT_TOKENS = 65_536;
|
|
3
|
+
const PROVIDER = "antigravity-guard";
|
|
3
4
|
const PUBLIC_MODEL = "antigravity-gemini-3.8-flash";
|
|
4
5
|
const WIRE_MODEL = "gemini-3.8-flash-tiered";
|
|
5
6
|
const RESERVED_HEADERS = new Set(["authorization", "host", "content-type", "content-length"]);
|
|
7
|
+
const THINKING_LEVELS = ["low", "medium", "high"];
|
|
6
8
|
export class ContextSerializationError extends Error {
|
|
7
9
|
}
|
|
8
10
|
export function serializeTextContext(input) {
|
|
@@ -20,10 +22,8 @@ export function serializeTextContext(input) {
|
|
|
20
22
|
const tools = field(context, "tools");
|
|
21
23
|
if (systemPrompt !== undefined && typeof systemPrompt !== "string")
|
|
22
24
|
fail("Text-only context is required.");
|
|
23
|
-
if (tools !== undefined)
|
|
24
|
-
|
|
25
|
-
fail("Tools are not supported by this text-only provider.");
|
|
26
|
-
}
|
|
25
|
+
if (tools !== undefined && isDenseArray(tools).length)
|
|
26
|
+
fail("Tools are not supported by this text-only provider.");
|
|
27
27
|
validateOptions(options);
|
|
28
28
|
let textBytes = byteLength(systemPrompt ?? "");
|
|
29
29
|
const contents = [];
|
|
@@ -35,11 +35,12 @@ export function serializeTextContext(input) {
|
|
|
35
35
|
fail("Tool history is not supported by this text-only provider.");
|
|
36
36
|
if (role !== "user" && role !== "assistant")
|
|
37
37
|
fail("Unsupported context role for this text-only provider.");
|
|
38
|
-
const
|
|
39
|
-
|
|
38
|
+
const assistant = role === "assistant";
|
|
39
|
+
const parts = messageParts(field(message, "content", true), assistant, assistant && isSameProviderAndModel(message));
|
|
40
|
+
textBytes += parts.reduce((total, part) => total + byteLength(part.text), 0);
|
|
40
41
|
if (textBytes > MAX_TEXT_BYTES)
|
|
41
42
|
fail("Text context is too large.");
|
|
42
|
-
contents.push({ role: role === "assistant" ? "model" : "user", parts
|
|
43
|
+
contents.push({ role: role === "assistant" ? "model" : "user", parts });
|
|
43
44
|
}
|
|
44
45
|
if (!contents.length)
|
|
45
46
|
fail("A text conversation is required.");
|
|
@@ -49,7 +50,7 @@ export function serializeTextContext(input) {
|
|
|
49
50
|
return { project, model: WIRE_MODEL, request: { contents, ...(systemInstruction ? { systemInstruction } : {}), generationConfig: {
|
|
50
51
|
temperature: typeof temperature === "number" ? temperature : 1,
|
|
51
52
|
maxOutputTokens: typeof maxTokens === "number" ? maxTokens : 4096,
|
|
52
|
-
thinkingConfig:
|
|
53
|
+
thinkingConfig: resolveThinkingConfig(options),
|
|
53
54
|
} }, requestType: "agent", userAgent: "antigravity", requestId };
|
|
54
55
|
}
|
|
55
56
|
catch (error) {
|
|
@@ -58,29 +59,69 @@ export function serializeTextContext(input) {
|
|
|
58
59
|
fail("Invalid text context.");
|
|
59
60
|
}
|
|
60
61
|
}
|
|
61
|
-
function
|
|
62
|
+
function messageParts(content, assistant, sameProviderAndModel) {
|
|
62
63
|
if (typeof content === "string")
|
|
63
64
|
return content ? [{ text: content }] : fail("A text conversation is required.");
|
|
64
65
|
const parts = isDenseArray(content);
|
|
65
66
|
if (!parts.length)
|
|
66
67
|
fail("A text conversation is required.");
|
|
67
68
|
return parts.map((part) => {
|
|
68
|
-
if (!isRecord(part)
|
|
69
|
-
fail("Only text context is supported by this provider.");
|
|
70
|
-
const text = field(part, "text", true);
|
|
71
|
-
if (typeof text !== "string" || !text)
|
|
69
|
+
if (!isRecord(part))
|
|
72
70
|
fail("Only text context is supported by this provider.");
|
|
73
|
-
|
|
71
|
+
const type = field(part, "type", true);
|
|
72
|
+
if (type === "text") {
|
|
73
|
+
const text = field(part, "text", true);
|
|
74
|
+
if (typeof text !== "string")
|
|
75
|
+
fail("Only text context is supported by this provider.");
|
|
76
|
+
const thoughtSignature = sameProviderAndModel ? validThoughtSignature(field(part, "textSignature")) : undefined;
|
|
77
|
+
if (!text && !thoughtSignature)
|
|
78
|
+
fail("Only text context is supported by this provider.");
|
|
79
|
+
return { text, ...(thoughtSignature ? { thoughtSignature } : {}) };
|
|
80
|
+
}
|
|
81
|
+
if (type === "thinking" && assistant && sameProviderAndModel) {
|
|
82
|
+
const text = field(part, "thinking", true);
|
|
83
|
+
if (typeof text !== "string")
|
|
84
|
+
fail("Only text context is supported by this provider.");
|
|
85
|
+
const thoughtSignature = validThoughtSignature(field(part, "thinkingSignature"));
|
|
86
|
+
if (!text && !thoughtSignature)
|
|
87
|
+
fail("Only text context is supported by this provider.");
|
|
88
|
+
return { thought: true, text, ...(thoughtSignature ? { thoughtSignature } : {}) };
|
|
89
|
+
}
|
|
90
|
+
if (type === "thinking" && assistant) {
|
|
91
|
+
const text = field(part, "thinking", true);
|
|
92
|
+
if (typeof text !== "string" || !text)
|
|
93
|
+
fail("Only text context is supported by this provider.");
|
|
94
|
+
return { text };
|
|
95
|
+
}
|
|
96
|
+
fail("Only text context is supported by this provider.");
|
|
74
97
|
});
|
|
75
98
|
}
|
|
99
|
+
function isSameProviderAndModel(message) {
|
|
100
|
+
return field(message, "provider") === PROVIDER && field(message, "model") === PUBLIC_MODEL;
|
|
101
|
+
}
|
|
102
|
+
function validThoughtSignature(value) {
|
|
103
|
+
if (typeof value !== "string" || !value || value.length % 4 !== 0)
|
|
104
|
+
return undefined;
|
|
105
|
+
return /^[A-Za-z0-9+/]+={0,2}$/.test(value) ? value : undefined;
|
|
106
|
+
}
|
|
107
|
+
function resolveThinkingConfig(options) {
|
|
108
|
+
const reasoning = option(options, "reasoning");
|
|
109
|
+
if (isThinkingLevel(reasoning))
|
|
110
|
+
return { thinkingLevel: reasoning, includeThoughts: true };
|
|
111
|
+
return { thinkingLevel: "low", includeThoughts: false };
|
|
112
|
+
}
|
|
76
113
|
function validateOptions(options) {
|
|
77
114
|
if (options === undefined)
|
|
78
115
|
return;
|
|
79
116
|
if (!isRecord(options))
|
|
80
117
|
fail("Invalid generation options.");
|
|
81
118
|
const reasoning = option(options, "reasoning"), budgets = option(options, "thinkingBudgets"), deferred = option(options, "deferred"), toolChoice = option(options, "toolChoice"), sampling = option(options, "samplingParams"), temperature = option(options, "temperature"), maxTokens = option(options, "maxTokens"), headers = option(options, "headers");
|
|
82
|
-
if (reasoning !== undefined
|
|
83
|
-
fail("
|
|
119
|
+
if (reasoning !== undefined && reasoning !== "off" && !isThinkingLevel(reasoning))
|
|
120
|
+
fail("Unsupported reasoning level.");
|
|
121
|
+
if (budgets !== undefined)
|
|
122
|
+
fail("Thinking budgets are not supported.");
|
|
123
|
+
if (deferred)
|
|
124
|
+
fail("Deferred requests are not supported.");
|
|
84
125
|
if (toolChoice !== undefined && toolChoice !== "none")
|
|
85
126
|
fail("Tools are not supported by this text-only provider.");
|
|
86
127
|
if (sampling !== undefined)
|
|
@@ -95,6 +136,9 @@ function validateOptions(options) {
|
|
|
95
136
|
if (RESERVED_HEADERS.has(name.toLowerCase()))
|
|
96
137
|
fail("Custom headers cannot replace protected request headers.");
|
|
97
138
|
}
|
|
139
|
+
function isThinkingLevel(value) {
|
|
140
|
+
return typeof value === "string" && THINKING_LEVELS.includes(value);
|
|
141
|
+
}
|
|
98
142
|
function byteLength(value) {
|
|
99
143
|
return new TextEncoder().encode(value).byteLength;
|
|
100
144
|
}
|
package/dist/provider.js
CHANGED
|
@@ -14,7 +14,8 @@ export function registerAntigravityProvider(pi) {
|
|
|
14
14
|
models: [{
|
|
15
15
|
id: MODEL,
|
|
16
16
|
name: "Gemini 3.8 Flash (Antigravity, text only)",
|
|
17
|
-
reasoning:
|
|
17
|
+
reasoning: true,
|
|
18
|
+
thinkingLevelMap: { minimal: null, low: "low", medium: "medium", high: "high" },
|
|
18
19
|
input: ["text"],
|
|
19
20
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
20
21
|
contextWindow: 1_048_576,
|
package/dist/response.d.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
export type ResponseSemantic = TextSemantic | FinishSemantic | UsageSemantic;
|
|
1
|
+
export type ResponseSemantic = TextSemantic | ThinkingSemantic | FinishSemantic | UsageSemantic;
|
|
2
2
|
export interface TextSemantic {
|
|
3
3
|
type: "text";
|
|
4
4
|
text: string;
|
|
5
|
+
signature?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ThinkingSemantic {
|
|
8
|
+
type: "thinking";
|
|
9
|
+
thinking: string;
|
|
10
|
+
signature?: string;
|
|
5
11
|
}
|
|
6
12
|
export interface FinishSemantic {
|
|
7
13
|
type: "finish";
|
|
@@ -13,18 +19,19 @@ export interface UsageSemantic {
|
|
|
13
19
|
output: number;
|
|
14
20
|
cacheRead: number;
|
|
15
21
|
cacheWrite: 0;
|
|
22
|
+
reasoning: number;
|
|
16
23
|
total: number;
|
|
17
24
|
}
|
|
18
25
|
export declare class ResponseSemanticError extends Error {
|
|
19
26
|
}
|
|
20
27
|
export declare class ResponseSemantics {
|
|
21
28
|
private finished;
|
|
22
|
-
private
|
|
29
|
+
private content;
|
|
23
30
|
private done;
|
|
24
31
|
private usage;
|
|
25
32
|
push(record: string): ResponseSemantic[];
|
|
26
33
|
finish(): void;
|
|
27
|
-
|
|
34
|
+
addContent(text: string): void;
|
|
28
35
|
addFinish(): void;
|
|
29
36
|
private updateUsage;
|
|
30
37
|
}
|
package/dist/response.js
CHANGED
|
@@ -2,9 +2,9 @@ export class ResponseSemanticError extends Error {
|
|
|
2
2
|
}
|
|
3
3
|
export class ResponseSemantics {
|
|
4
4
|
finished = false;
|
|
5
|
-
|
|
5
|
+
content = false;
|
|
6
6
|
done = false;
|
|
7
|
-
usage = { prompt: 0, cacheRead: 0, output: 0 };
|
|
7
|
+
usage = { prompt: 0, cacheRead: 0, output: 0, reasoning: 0 };
|
|
8
8
|
push(record) {
|
|
9
9
|
if (this.done)
|
|
10
10
|
throw new ResponseSemanticError("Response data arrived after [DONE].");
|
|
@@ -35,20 +35,22 @@ export class ResponseSemantics {
|
|
|
35
35
|
finish() {
|
|
36
36
|
if (!this.finished)
|
|
37
37
|
throw new ResponseSemanticError("Response ended without a finish reason.");
|
|
38
|
-
if (!this.
|
|
39
|
-
throw new ResponseSemanticError("Response ended without
|
|
38
|
+
if (!this.content)
|
|
39
|
+
throw new ResponseSemanticError("Response ended without content.");
|
|
40
40
|
}
|
|
41
|
-
|
|
41
|
+
addContent(text) { this.content = this.content || text.length > 0; }
|
|
42
42
|
addFinish() { this.finished = true; }
|
|
43
43
|
updateUsage(value) {
|
|
44
44
|
const prompt = count(value, "promptTokenCount", this.usage.prompt);
|
|
45
45
|
const cacheRead = count(value, "cachedContentTokenCount", this.usage.cacheRead);
|
|
46
|
-
const
|
|
46
|
+
const candidates = count(value, "candidatesTokenCount", this.usage.output - this.usage.reasoning);
|
|
47
|
+
const reasoning = count(value, "thoughtsTokenCount", this.usage.reasoning);
|
|
48
|
+
const output = candidates + reasoning;
|
|
47
49
|
const reported = field(value, "totalTokenCount");
|
|
48
50
|
if (cacheRead > prompt || (reported !== undefined && reported !== prompt + output))
|
|
49
51
|
throw new ResponseSemanticError("Antigravity returned invalid usage.");
|
|
50
|
-
this.usage = { prompt, cacheRead, output };
|
|
51
|
-
return { type: "usage", input: prompt - cacheRead, output, cacheRead, cacheWrite: 0, total: prompt + output };
|
|
52
|
+
this.usage = { prompt, cacheRead, output, reasoning };
|
|
53
|
+
return { type: "usage", input: prompt - cacheRead, output, cacheRead, cacheWrite: 0, reasoning, total: prompt + output };
|
|
52
54
|
}
|
|
53
55
|
}
|
|
54
56
|
function candidate(value, events, semantics) {
|
|
@@ -67,10 +69,15 @@ function candidate(value, events, semantics) {
|
|
|
67
69
|
throw new ResponseSemanticError("Antigravity returned invalid content.");
|
|
68
70
|
for (const part of parts) {
|
|
69
71
|
const text = field(part, "text");
|
|
70
|
-
|
|
72
|
+
const thought = field(part, "thought");
|
|
73
|
+
if (typeof text !== "string" || (thought !== undefined && typeof thought !== "boolean") || Object.keys(part).some((name) => name !== "text" && name !== "thought" && name !== "thoughtSignature"))
|
|
71
74
|
throw new ResponseSemanticError("Antigravity returned unsupported content.");
|
|
72
|
-
|
|
73
|
-
|
|
75
|
+
const signature = validThoughtSignature(field(part, "thoughtSignature"));
|
|
76
|
+
semantics.addContent(text);
|
|
77
|
+
if (thought === true)
|
|
78
|
+
events.push({ type: "thinking", thinking: text, ...(signature ? { signature } : {}) });
|
|
79
|
+
else
|
|
80
|
+
events.push({ type: "text", text, ...(signature ? { signature } : {}) });
|
|
74
81
|
}
|
|
75
82
|
}
|
|
76
83
|
const finishReason = field(candidate, "finishReason");
|
|
@@ -81,6 +88,11 @@ function candidate(value, events, semantics) {
|
|
|
81
88
|
events.push({ type: "finish", reason: finishReason === "STOP" ? "stop" : "length" });
|
|
82
89
|
}
|
|
83
90
|
}
|
|
91
|
+
function validThoughtSignature(value) {
|
|
92
|
+
if (typeof value !== "string" || !value || value.length % 4 !== 0)
|
|
93
|
+
return undefined;
|
|
94
|
+
return /^[A-Za-z0-9+/]+={0,2}$/.test(value) ? value : undefined;
|
|
95
|
+
}
|
|
84
96
|
function validateMetadata(value) {
|
|
85
97
|
for (const name of ["responseId", "modelVersion"]) {
|
|
86
98
|
const metadata = field(value, name);
|
package/dist/stream.js
CHANGED
|
@@ -37,6 +37,17 @@ export function createPiLifecycleStream(input) {
|
|
|
37
37
|
};
|
|
38
38
|
let complete = false;
|
|
39
39
|
let textStarted = false;
|
|
40
|
+
let currentBlock;
|
|
41
|
+
const closeBlock = () => {
|
|
42
|
+
if (!currentBlock)
|
|
43
|
+
return;
|
|
44
|
+
const contentIndex = output.content.length - 1;
|
|
45
|
+
if (currentBlock.type === "text")
|
|
46
|
+
stream.push({ type: "text_end", contentIndex, content: currentBlock.text, partial: output });
|
|
47
|
+
else
|
|
48
|
+
stream.push({ type: "thinking_end", contentIndex, content: currentBlock.thinking, partial: output });
|
|
49
|
+
currentBlock = undefined;
|
|
50
|
+
};
|
|
40
51
|
let removeAbort = () => { };
|
|
41
52
|
const finalize = (reason, errorMessage) => {
|
|
42
53
|
if (complete)
|
|
@@ -47,8 +58,7 @@ export function createPiLifecycleStream(input) {
|
|
|
47
58
|
controller.abort();
|
|
48
59
|
output.stopReason = reason;
|
|
49
60
|
if (reason === "stop" || reason === "length") {
|
|
50
|
-
|
|
51
|
-
stream.push({ type: "text_end", contentIndex: 0, content: output.content[0]?.type === "text" ? output.content[0].text : "", partial: output });
|
|
61
|
+
closeBlock();
|
|
52
62
|
stream.push({ type: "done", reason, message: output });
|
|
53
63
|
}
|
|
54
64
|
else {
|
|
@@ -60,6 +70,35 @@ export function createPiLifecycleStream(input) {
|
|
|
60
70
|
const onSemantic = (semantic) => {
|
|
61
71
|
if (complete)
|
|
62
72
|
return;
|
|
73
|
+
if (isContentSemantic(semantic)) {
|
|
74
|
+
if (!currentBlock || currentBlock.type !== semantic.type) {
|
|
75
|
+
closeBlock();
|
|
76
|
+
if (semantic.type === "text") {
|
|
77
|
+
currentBlock = { type: "text", text: "" };
|
|
78
|
+
output.content.push(currentBlock);
|
|
79
|
+
stream.push({ type: "text_start", contentIndex: output.content.length - 1, partial: output });
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
currentBlock = { type: "thinking", thinking: "" };
|
|
83
|
+
output.content.push(currentBlock);
|
|
84
|
+
stream.push({ type: "thinking_start", contentIndex: output.content.length - 1, partial: output });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const contentIndex = output.content.length - 1;
|
|
88
|
+
if (semantic.type === "text" && currentBlock.type === "text") {
|
|
89
|
+
currentBlock.text += semantic.text;
|
|
90
|
+
if (semantic.signature)
|
|
91
|
+
currentBlock.textSignature = semantic.signature;
|
|
92
|
+
stream.push({ type: "text_delta", contentIndex, delta: semantic.text, partial: output });
|
|
93
|
+
}
|
|
94
|
+
else if (semantic.type === "thinking" && currentBlock.type === "thinking") {
|
|
95
|
+
currentBlock.thinking += semantic.thinking;
|
|
96
|
+
if (semantic.signature)
|
|
97
|
+
currentBlock.thinkingSignature = semantic.signature;
|
|
98
|
+
stream.push({ type: "thinking_delta", contentIndex, delta: semantic.thinking, partial: output });
|
|
99
|
+
}
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
63
102
|
if (semantic.type === "text" && semantic.text) {
|
|
64
103
|
if (!textStarted) {
|
|
65
104
|
textStarted = true;
|
|
@@ -76,6 +115,7 @@ export function createPiLifecycleStream(input) {
|
|
|
76
115
|
output.usage.output = semantic.output;
|
|
77
116
|
output.usage.cacheRead = semantic.cacheRead;
|
|
78
117
|
output.usage.cacheWrite = semantic.cacheWrite;
|
|
118
|
+
output.usage.reasoning = semantic.reasoning;
|
|
79
119
|
output.usage.totalTokens = semantic.input + semantic.output + semantic.cacheRead + semantic.cacheWrite;
|
|
80
120
|
output.usage.cost = calculateCost(input.model, output.usage);
|
|
81
121
|
}
|
|
@@ -95,6 +135,9 @@ export function createPiLifecycleStream(input) {
|
|
|
95
135
|
finalize(signal.aborted ? "aborted" : "error"); }, (error) => finalize(signal.aborted || input.signal?.aborted ? "aborted" : "error", isLocalStreamError(error) ? error.message : undefined));
|
|
96
136
|
return stream;
|
|
97
137
|
}
|
|
138
|
+
function isContentSemantic(semantic) {
|
|
139
|
+
return semantic.type === "text" || semantic.type === "thinking";
|
|
140
|
+
}
|
|
98
141
|
export async function executeStreamTransport(input) {
|
|
99
142
|
validateInput(input);
|
|
100
143
|
const signal = totalSignal(input);
|