smoltalk 0.0.34 → 0.0.36
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/dist/classes/message/AssistantMessage.d.ts +9 -1
- package/dist/classes/message/AssistantMessage.js +23 -2
- package/dist/classes/message/index.d.ts +4 -0
- package/dist/clients/anthropic.js +50 -10
- package/dist/clients/baseClient.d.ts +2 -0
- package/dist/clients/baseClient.js +19 -3
- package/dist/clients/google.js +33 -16
- package/dist/functions.js +2 -1
- package/dist/statelogClient.d.ts +113 -0
- package/dist/statelogClient.js +303 -0
- package/dist/types.d.ts +26 -0
- package/package.json +3 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BaseMessage, MessageClass } from "./BaseMessage.js";
|
|
2
|
-
import { TextPart } from "../../types.js";
|
|
2
|
+
import { TextPart, ThinkingBlock } from "../../types.js";
|
|
3
3
|
import { ChatCompletionMessageParam } from "openai/resources";
|
|
4
4
|
import { Content } from "@google/genai";
|
|
5
5
|
import { ToolCall, ToolCallJSON } from "../ToolCall.js";
|
|
@@ -12,6 +12,7 @@ export type AssistantMessageJSON = {
|
|
|
12
12
|
audio: any | null | undefined;
|
|
13
13
|
refusal: string | null | undefined;
|
|
14
14
|
toolCalls: ToolCallJSON[] | undefined;
|
|
15
|
+
thinkingBlocks: ThinkingBlock[] | undefined;
|
|
15
16
|
};
|
|
16
17
|
export declare class AssistantMessage extends BaseMessage implements MessageClass {
|
|
17
18
|
_role: "assistant";
|
|
@@ -20,12 +21,14 @@ export declare class AssistantMessage extends BaseMessage implements MessageClas
|
|
|
20
21
|
_audio?: any | null;
|
|
21
22
|
_refusal?: string | null;
|
|
22
23
|
_toolCalls?: ToolCall[];
|
|
24
|
+
_thinkingBlocks?: ThinkingBlock[];
|
|
23
25
|
_rawData?: any;
|
|
24
26
|
constructor(content: string | Array<TextPart> | null, options?: {
|
|
25
27
|
name?: string;
|
|
26
28
|
audio?: any | null;
|
|
27
29
|
refusal?: string | null;
|
|
28
30
|
toolCalls?: ToolCall[];
|
|
31
|
+
thinkingBlocks?: ThinkingBlock[];
|
|
29
32
|
rawData?: any;
|
|
30
33
|
});
|
|
31
34
|
get content(): string;
|
|
@@ -36,6 +39,7 @@ export declare class AssistantMessage extends BaseMessage implements MessageClas
|
|
|
36
39
|
get refusal(): string | null | undefined;
|
|
37
40
|
get toolCalls(): ToolCall[] | undefined;
|
|
38
41
|
get rawData(): any;
|
|
42
|
+
get thinkingBlocks(): ThinkingBlock[] | undefined;
|
|
39
43
|
toJSON(): AssistantMessageJSON;
|
|
40
44
|
static fromJSON(json: any): AssistantMessage;
|
|
41
45
|
toOpenAIMessage(): ChatCompletionMessageParam;
|
|
@@ -45,6 +49,10 @@ export declare class AssistantMessage extends BaseMessage implements MessageClas
|
|
|
45
49
|
toAnthropicMessage(): {
|
|
46
50
|
role: "assistant";
|
|
47
51
|
content: string | Array<{
|
|
52
|
+
type: "thinking";
|
|
53
|
+
thinking: string;
|
|
54
|
+
signature: string;
|
|
55
|
+
} | {
|
|
48
56
|
type: "text";
|
|
49
57
|
text: string;
|
|
50
58
|
} | {
|
|
@@ -7,6 +7,7 @@ export class AssistantMessage extends BaseMessage {
|
|
|
7
7
|
_audio;
|
|
8
8
|
_refusal;
|
|
9
9
|
_toolCalls;
|
|
10
|
+
_thinkingBlocks;
|
|
10
11
|
_rawData;
|
|
11
12
|
constructor(content, options = {}) {
|
|
12
13
|
super();
|
|
@@ -15,6 +16,7 @@ export class AssistantMessage extends BaseMessage {
|
|
|
15
16
|
this._audio = options.audio;
|
|
16
17
|
this._refusal = options.refusal;
|
|
17
18
|
this._toolCalls = options.toolCalls;
|
|
19
|
+
this._thinkingBlocks = options.thinkingBlocks;
|
|
18
20
|
this._rawData = options.rawData;
|
|
19
21
|
}
|
|
20
22
|
get content() {
|
|
@@ -46,6 +48,9 @@ export class AssistantMessage extends BaseMessage {
|
|
|
46
48
|
get rawData() {
|
|
47
49
|
return this._rawData;
|
|
48
50
|
}
|
|
51
|
+
get thinkingBlocks() {
|
|
52
|
+
return this._thinkingBlocks;
|
|
53
|
+
}
|
|
49
54
|
toJSON() {
|
|
50
55
|
return {
|
|
51
56
|
role: this.role,
|
|
@@ -54,6 +59,7 @@ export class AssistantMessage extends BaseMessage {
|
|
|
54
59
|
audio: this.audio,
|
|
55
60
|
refusal: this.refusal,
|
|
56
61
|
toolCalls: this.toolCalls?.map((tc) => tc.toJSON()),
|
|
62
|
+
thinkingBlocks: this._thinkingBlocks,
|
|
57
63
|
};
|
|
58
64
|
}
|
|
59
65
|
static fromJSON(json) {
|
|
@@ -64,6 +70,7 @@ export class AssistantMessage extends BaseMessage {
|
|
|
64
70
|
toolCalls: json.toolCalls
|
|
65
71
|
? json.toolCalls.map((tcJson) => ToolCall.fromJSON(tcJson))
|
|
66
72
|
: undefined,
|
|
73
|
+
thinkingBlocks: json.thinkingBlocks,
|
|
67
74
|
rawData: json.rawData,
|
|
68
75
|
});
|
|
69
76
|
}
|
|
@@ -95,6 +102,12 @@ export class AssistantMessage extends BaseMessage {
|
|
|
95
102
|
}
|
|
96
103
|
toGoogleMessage() {
|
|
97
104
|
const parts = [];
|
|
105
|
+
// Prepend thought parts with their signatures so Gemini can resume reasoning
|
|
106
|
+
if (this._thinkingBlocks) {
|
|
107
|
+
for (const block of this._thinkingBlocks) {
|
|
108
|
+
parts.push({ thought: true, text: block.text, thoughtSignature: block.signature });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
98
111
|
if (this.content) {
|
|
99
112
|
parts.push({ text: this.content });
|
|
100
113
|
}
|
|
@@ -119,10 +132,18 @@ export class AssistantMessage extends BaseMessage {
|
|
|
119
132
|
? this._content.length > 0
|
|
120
133
|
: this._content.length > 0);
|
|
121
134
|
const hasToolCalls = this._toolCalls && this._toolCalls.length > 0;
|
|
122
|
-
|
|
135
|
+
const hasThinking = this._thinkingBlocks && this._thinkingBlocks.length > 0;
|
|
136
|
+
// If only text and no thinking/tool calls, use string shorthand
|
|
137
|
+
if (!hasToolCalls && !hasThinking) {
|
|
123
138
|
return { role: "assistant", content: this.content };
|
|
124
139
|
}
|
|
125
140
|
const blocks = [];
|
|
141
|
+
// Thinking blocks must come first (Anthropic requires this ordering)
|
|
142
|
+
if (hasThinking) {
|
|
143
|
+
for (const block of this._thinkingBlocks) {
|
|
144
|
+
blocks.push({ type: "thinking", thinking: block.text, signature: block.signature });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
126
147
|
if (hasText) {
|
|
127
148
|
const text = typeof this._content === "string"
|
|
128
149
|
? this._content
|
|
@@ -131,7 +152,7 @@ export class AssistantMessage extends BaseMessage {
|
|
|
131
152
|
blocks.push({ type: "text", text });
|
|
132
153
|
}
|
|
133
154
|
}
|
|
134
|
-
for (const tc of this._toolCalls) {
|
|
155
|
+
for (const tc of this._toolCalls ?? []) {
|
|
135
156
|
blocks.push({ type: "tool_use", id: tc.id, name: tc.name, input: tc.arguments });
|
|
136
157
|
}
|
|
137
158
|
return { role: "assistant", content: blocks };
|
|
@@ -25,6 +25,10 @@ export declare function assistantMessage(content: string | Array<TextPart> | nul
|
|
|
25
25
|
audio?: any | null;
|
|
26
26
|
refusal?: string | null;
|
|
27
27
|
toolCalls?: Array<any>;
|
|
28
|
+
thinkingBlocks?: Array<{
|
|
29
|
+
text: string;
|
|
30
|
+
signature: string;
|
|
31
|
+
}>;
|
|
28
32
|
rawData?: any;
|
|
29
33
|
}): AssistantMessage;
|
|
30
34
|
export declare function developerMessage(content: string | Array<TextPart>, options?: {
|
|
@@ -64,16 +64,20 @@ export class SmolAnthropic extends BaseClient {
|
|
|
64
64
|
description: tool.description,
|
|
65
65
|
}))
|
|
66
66
|
: undefined;
|
|
67
|
-
|
|
67
|
+
const thinking = config.thinking?.enabled
|
|
68
|
+
? { type: "enabled", budget_tokens: config.thinking.budgetTokens ?? 5000 }
|
|
69
|
+
: undefined;
|
|
70
|
+
return { system, messages: anthropicMessages, tools, thinking };
|
|
68
71
|
}
|
|
69
72
|
async _textSync(config) {
|
|
70
|
-
const { system, messages, tools } = this.buildRequest(config);
|
|
73
|
+
const { system, messages, tools, thinking } = this.buildRequest(config);
|
|
71
74
|
this.logger.debug("Sending request to Anthropic:", {
|
|
72
75
|
model: this.model,
|
|
73
76
|
max_tokens: config.maxTokens ?? DEFAULT_MAX_TOKENS,
|
|
74
77
|
messages,
|
|
75
78
|
system,
|
|
76
79
|
tools,
|
|
80
|
+
thinking,
|
|
77
81
|
});
|
|
78
82
|
const response = await this.client.messages.create({
|
|
79
83
|
model: this.model,
|
|
@@ -81,6 +85,7 @@ export class SmolAnthropic extends BaseClient {
|
|
|
81
85
|
messages,
|
|
82
86
|
...(system && { system }),
|
|
83
87
|
...(tools && { tools }),
|
|
88
|
+
...(thinking && { thinking }),
|
|
84
89
|
...(config.temperature !== undefined && {
|
|
85
90
|
temperature: config.temperature,
|
|
86
91
|
}),
|
|
@@ -90,6 +95,7 @@ export class SmolAnthropic extends BaseClient {
|
|
|
90
95
|
this.logger.debug("Response from Anthropic:", response);
|
|
91
96
|
let output = null;
|
|
92
97
|
const toolCalls = [];
|
|
98
|
+
const thinkingBlocks = [];
|
|
93
99
|
for (const block of response.content) {
|
|
94
100
|
if (block.type === "text") {
|
|
95
101
|
output = (output ?? "") + block.text;
|
|
@@ -97,24 +103,30 @@ export class SmolAnthropic extends BaseClient {
|
|
|
97
103
|
else if (block.type === "tool_use") {
|
|
98
104
|
toolCalls.push(new ToolCall(block.id, block.name, block.input));
|
|
99
105
|
}
|
|
106
|
+
else if (block.type === "thinking") {
|
|
107
|
+
const b = block;
|
|
108
|
+
thinkingBlocks.push({ text: b.thinking, signature: b.signature });
|
|
109
|
+
}
|
|
100
110
|
}
|
|
101
111
|
const { usage, cost } = this.calculateUsageAndCost(response.usage);
|
|
102
112
|
return success({
|
|
103
113
|
output,
|
|
104
114
|
toolCalls,
|
|
115
|
+
...(thinkingBlocks.length > 0 && { thinkingBlocks }),
|
|
105
116
|
usage,
|
|
106
117
|
cost,
|
|
107
118
|
model: this.model,
|
|
108
119
|
});
|
|
109
120
|
}
|
|
110
121
|
async *_textStream(config) {
|
|
111
|
-
const { system, messages, tools } = this.buildRequest(config);
|
|
122
|
+
const { system, messages, tools, thinking } = this.buildRequest(config);
|
|
112
123
|
this.logger.debug("Sending streaming request to Anthropic:", {
|
|
113
124
|
model: this.model,
|
|
114
125
|
max_tokens: config.maxTokens ?? DEFAULT_MAX_TOKENS,
|
|
115
126
|
messages,
|
|
116
127
|
system,
|
|
117
128
|
tools,
|
|
129
|
+
thinking,
|
|
118
130
|
});
|
|
119
131
|
const stream = await this.client.messages.create({
|
|
120
132
|
model: this.model,
|
|
@@ -122,6 +134,7 @@ export class SmolAnthropic extends BaseClient {
|
|
|
122
134
|
messages,
|
|
123
135
|
...(system && { system }),
|
|
124
136
|
...(tools && { tools }),
|
|
137
|
+
...(thinking && { thinking }),
|
|
125
138
|
...(config.temperature !== undefined && {
|
|
126
139
|
temperature: config.temperature,
|
|
127
140
|
}),
|
|
@@ -131,19 +144,25 @@ export class SmolAnthropic extends BaseClient {
|
|
|
131
144
|
let content = "";
|
|
132
145
|
// Track tool blocks by index: index -> { id, name, arguments (partial JSON) }
|
|
133
146
|
const toolBlocks = new Map();
|
|
147
|
+
// Track thinking blocks by index: index -> { text, signature }
|
|
148
|
+
const thinkingBlockMap = new Map();
|
|
134
149
|
let inputTokens = 0;
|
|
135
150
|
let outputTokens = 0;
|
|
136
151
|
for await (const event of stream) {
|
|
137
152
|
if (event.type === "message_start") {
|
|
138
153
|
inputTokens = event.message.usage.input_tokens;
|
|
139
154
|
}
|
|
140
|
-
else if (event.type === "content_block_start"
|
|
141
|
-
event.content_block.type === "tool_use") {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
155
|
+
else if (event.type === "content_block_start") {
|
|
156
|
+
if (event.content_block.type === "tool_use") {
|
|
157
|
+
toolBlocks.set(event.index, {
|
|
158
|
+
id: event.content_block.id,
|
|
159
|
+
name: event.content_block.name,
|
|
160
|
+
arguments: "",
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
else if (event.content_block.type === "thinking") {
|
|
164
|
+
thinkingBlockMap.set(event.index, { text: "", signature: "" });
|
|
165
|
+
}
|
|
147
166
|
}
|
|
148
167
|
else if (event.type === "content_block_delta") {
|
|
149
168
|
if (event.delta.type === "text_delta") {
|
|
@@ -156,6 +175,25 @@ export class SmolAnthropic extends BaseClient {
|
|
|
156
175
|
block.arguments += event.delta.partial_json;
|
|
157
176
|
}
|
|
158
177
|
}
|
|
178
|
+
else if (event.delta.type === "thinking_delta") {
|
|
179
|
+
const block = thinkingBlockMap.get(event.index);
|
|
180
|
+
if (block) {
|
|
181
|
+
block.text += event.delta.thinking;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
else if (event.delta.type === "signature_delta") {
|
|
185
|
+
const block = thinkingBlockMap.get(event.index);
|
|
186
|
+
if (block) {
|
|
187
|
+
block.signature = event.delta.signature;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
else if (event.type === "content_block_stop") {
|
|
192
|
+
// Emit thinking chunk once the block is fully assembled
|
|
193
|
+
const thinkingBlock = thinkingBlockMap.get(event.index);
|
|
194
|
+
if (thinkingBlock) {
|
|
195
|
+
yield { type: "thinking", text: thinkingBlock.text, signature: thinkingBlock.signature };
|
|
196
|
+
}
|
|
159
197
|
}
|
|
160
198
|
else if (event.type === "message_delta") {
|
|
161
199
|
outputTokens = event.usage.output_tokens;
|
|
@@ -168,6 +206,7 @@ export class SmolAnthropic extends BaseClient {
|
|
|
168
206
|
toolCalls.push(toolCall);
|
|
169
207
|
yield { type: "tool_call", toolCall };
|
|
170
208
|
}
|
|
209
|
+
const thinkingBlocks = Array.from(thinkingBlockMap.values());
|
|
171
210
|
const usage = {
|
|
172
211
|
inputTokens,
|
|
173
212
|
outputTokens,
|
|
@@ -179,6 +218,7 @@ export class SmolAnthropic extends BaseClient {
|
|
|
179
218
|
result: {
|
|
180
219
|
output: content || null,
|
|
181
220
|
toolCalls,
|
|
221
|
+
...(thinkingBlocks.length > 0 && { thinkingBlocks }),
|
|
182
222
|
usage,
|
|
183
223
|
cost,
|
|
184
224
|
model: this.model,
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { StatelogClient } from "../statelogClient.js";
|
|
1
2
|
import { PromptConfig, PromptResult, Result, SmolClient, SmolConfig, StreamChunk } from "../types.js";
|
|
2
3
|
export declare class BaseClient implements SmolClient {
|
|
3
4
|
protected config: SmolConfig;
|
|
5
|
+
protected statelogClient?: StatelogClient;
|
|
4
6
|
constructor(config: SmolConfig);
|
|
5
7
|
text(promptConfig: Omit<PromptConfig, "stream">): Promise<Result<PromptResult>>;
|
|
6
8
|
text(promptConfig: Omit<PromptConfig, "stream"> & {
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { userMessage, assistantMessage } from "../classes/message/index.js";
|
|
2
2
|
import { getLogger } from "../logger.js";
|
|
3
|
+
import { getStatelogClient } from "../statelogClient.js";
|
|
3
4
|
import { success, } from "../types.js";
|
|
4
5
|
import { z } from "zod";
|
|
5
6
|
const DEFAULT_NUM_RETRIES = 2;
|
|
6
7
|
export class BaseClient {
|
|
7
8
|
config;
|
|
9
|
+
statelogClient;
|
|
8
10
|
constructor(config) {
|
|
9
11
|
this.config = config || {};
|
|
12
|
+
if (this.config.statelog) {
|
|
13
|
+
this.statelogClient = getStatelogClient(this.config.statelog);
|
|
14
|
+
}
|
|
10
15
|
}
|
|
11
16
|
text(promptConfig) {
|
|
12
17
|
if (promptConfig.stream) {
|
|
@@ -39,7 +44,8 @@ export class BaseClient {
|
|
|
39
44
|
value: { output: null, toolCalls: [], model: this.config.model },
|
|
40
45
|
};
|
|
41
46
|
}
|
|
42
|
-
|
|
47
|
+
const result = await this.textWithRetry(newPromptConfig, newPromptConfig.responseFormatOptions?.numRetries || DEFAULT_NUM_RETRIES);
|
|
48
|
+
return result;
|
|
43
49
|
}
|
|
44
50
|
checkForToolLoops(promptConfig) {
|
|
45
51
|
if (!this.config.toolLoopDetection?.enabled) {
|
|
@@ -57,6 +63,11 @@ export class BaseClient {
|
|
|
57
63
|
const intervention = this.config.toolLoopDetection.intervention || "remove-tool";
|
|
58
64
|
const logger = getLogger();
|
|
59
65
|
logger.warn(`Tool loop detected for tool "${toolName}" called ${count} times. Intervention: ${intervention}`);
|
|
66
|
+
this.statelogClient?.debug("Tool loop detected", {
|
|
67
|
+
toolName,
|
|
68
|
+
count,
|
|
69
|
+
intervention,
|
|
70
|
+
});
|
|
60
71
|
switch (intervention) {
|
|
61
72
|
case "remove-tool":
|
|
62
73
|
const newTools = promptConfig.tools?.filter((t) => t.name !== toolName);
|
|
@@ -167,10 +178,15 @@ export class BaseClient {
|
|
|
167
178
|
catch (err) {
|
|
168
179
|
const errorMessage = err.message;
|
|
169
180
|
const logger = getLogger();
|
|
170
|
-
logger.
|
|
181
|
+
logger.warn(`Response format validation failed (retries left: ${retries}): `, errorMessage, "output:", JSON.stringify(output, null, 2), "responseFormat:", JSON.stringify(promptConfig.responseFormat, null, 2));
|
|
171
182
|
if (err instanceof z.ZodError) {
|
|
172
|
-
logger.
|
|
183
|
+
logger.warn("Zod error details:", z.prettifyError(err));
|
|
173
184
|
}
|
|
185
|
+
this.statelogClient?.diff({
|
|
186
|
+
message: "Response format validation failed",
|
|
187
|
+
itemA: promptConfig.responseFormat,
|
|
188
|
+
itemB: output,
|
|
189
|
+
});
|
|
174
190
|
const retryMessages = [
|
|
175
191
|
...promptConfig.messages,
|
|
176
192
|
assistantMessage(output),
|
package/dist/clients/google.js
CHANGED
|
@@ -91,6 +91,7 @@ export class SmolGoogle extends BaseClient {
|
|
|
91
91
|
this.logger.debug("Response from Google Gemini:", JSON.stringify(result, null, 2));
|
|
92
92
|
const output = result.text || null;
|
|
93
93
|
const toolCalls = [];
|
|
94
|
+
const thinkingBlocks = [];
|
|
94
95
|
result.candidates?.forEach((candidate) => {
|
|
95
96
|
if (candidate.content && candidate.content.parts) {
|
|
96
97
|
candidate.content.parts.forEach((part) => {
|
|
@@ -98,6 +99,13 @@ export class SmolGoogle extends BaseClient {
|
|
|
98
99
|
const functionCall = part.functionCall;
|
|
99
100
|
toolCalls.push(new ToolCall("", functionCall.name, functionCall.args));
|
|
100
101
|
}
|
|
102
|
+
// Capture thought parts (thought: true indicates a thinking part)
|
|
103
|
+
if (part.thoughtSignature) {
|
|
104
|
+
thinkingBlocks.push({
|
|
105
|
+
text: part.text || "",
|
|
106
|
+
signature: part.thoughtSignature,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
101
109
|
});
|
|
102
110
|
}
|
|
103
111
|
});
|
|
@@ -107,6 +115,7 @@ export class SmolGoogle extends BaseClient {
|
|
|
107
115
|
return success({
|
|
108
116
|
output,
|
|
109
117
|
toolCalls,
|
|
118
|
+
...(thinkingBlocks.length > 0 && { thinkingBlocks }),
|
|
110
119
|
usage,
|
|
111
120
|
cost,
|
|
112
121
|
model: request.model,
|
|
@@ -118,6 +127,7 @@ export class SmolGoogle extends BaseClient {
|
|
|
118
127
|
const stream = await this.client.models.generateContentStream(request);
|
|
119
128
|
let content = "";
|
|
120
129
|
const toolCallsMap = new Map();
|
|
130
|
+
const thinkingBlocks = [];
|
|
121
131
|
let usage;
|
|
122
132
|
let cost;
|
|
123
133
|
for await (const chunk of stream) {
|
|
@@ -127,22 +137,28 @@ export class SmolGoogle extends BaseClient {
|
|
|
127
137
|
usage = usageAndCost.usage;
|
|
128
138
|
cost = usageAndCost.cost;
|
|
129
139
|
}
|
|
130
|
-
//
|
|
131
|
-
|
|
132
|
-
content
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
140
|
+
// Iterate raw parts to capture thought signatures and regular content
|
|
141
|
+
for (const candidate of chunk.candidates || []) {
|
|
142
|
+
for (const part of candidate?.content?.parts || []) {
|
|
143
|
+
const p = part;
|
|
144
|
+
if (p.thoughtSignature) {
|
|
145
|
+
const block = {
|
|
146
|
+
text: p.text || "",
|
|
147
|
+
signature: p.thoughtSignature,
|
|
148
|
+
};
|
|
149
|
+
thinkingBlocks.push(block);
|
|
150
|
+
yield { type: "thinking", text: block.text, signature: block.signature };
|
|
151
|
+
}
|
|
152
|
+
else if (p.text) {
|
|
153
|
+
content += p.text;
|
|
154
|
+
yield { type: "text", text: p.text };
|
|
155
|
+
}
|
|
156
|
+
else if (p.functionCall) {
|
|
157
|
+
const id = p.functionCall.id || p.functionCall.name || "";
|
|
158
|
+
const name = p.functionCall.name || "";
|
|
159
|
+
if (!toolCallsMap.has(id)) {
|
|
160
|
+
toolCallsMap.set(id, { id, name, arguments: p.functionCall.args });
|
|
161
|
+
}
|
|
146
162
|
}
|
|
147
163
|
}
|
|
148
164
|
}
|
|
@@ -160,6 +176,7 @@ export class SmolGoogle extends BaseClient {
|
|
|
160
176
|
result: {
|
|
161
177
|
output: content || null,
|
|
162
178
|
toolCalls,
|
|
179
|
+
...(thinkingBlocks.length > 0 && { thinkingBlocks }),
|
|
163
180
|
usage,
|
|
164
181
|
cost,
|
|
165
182
|
model: request.model,
|
package/dist/functions.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getClient } from "./client.js";
|
|
2
2
|
import { isModelConfig, pickModel } from "./models.js";
|
|
3
3
|
function splitConfig(config) {
|
|
4
|
-
const { openAiApiKey, googleApiKey, ollamaApiKey, anthropicApiKey, ollamaHost, model: rawModel, provider, logLevel, toolLoopDetection, ...promptConfig } = config;
|
|
4
|
+
const { openAiApiKey, googleApiKey, ollamaApiKey, anthropicApiKey, ollamaHost, model: rawModel, provider, logLevel, toolLoopDetection, statelog, ...promptConfig } = config;
|
|
5
5
|
const model = isModelConfig(rawModel) ? pickModel(rawModel) : rawModel;
|
|
6
6
|
return {
|
|
7
7
|
smolConfig: {
|
|
@@ -14,6 +14,7 @@ function splitConfig(config) {
|
|
|
14
14
|
provider,
|
|
15
15
|
logLevel,
|
|
16
16
|
toolLoopDetection,
|
|
17
|
+
statelog,
|
|
17
18
|
},
|
|
18
19
|
promptConfig,
|
|
19
20
|
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { JSONEdge } from "./types.js";
|
|
2
|
+
import { Result } from "./types/result.js";
|
|
3
|
+
import { ModelConfig, ModelName } from "./models.js";
|
|
4
|
+
export type AgencyFile = {
|
|
5
|
+
name: string;
|
|
6
|
+
contents: string;
|
|
7
|
+
};
|
|
8
|
+
export type UploadResult = Result<{
|
|
9
|
+
endpointUrls: string[];
|
|
10
|
+
}>;
|
|
11
|
+
export type StatelogConfig = {
|
|
12
|
+
host: string;
|
|
13
|
+
traceId?: string;
|
|
14
|
+
apiKey: string;
|
|
15
|
+
projectId: string;
|
|
16
|
+
debugMode: boolean;
|
|
17
|
+
};
|
|
18
|
+
export declare function mergeUploadResults(_results: UploadResult[]): UploadResult;
|
|
19
|
+
export declare class StatelogClient {
|
|
20
|
+
private host;
|
|
21
|
+
private debugMode;
|
|
22
|
+
private traceId;
|
|
23
|
+
private apiKey;
|
|
24
|
+
private projectId;
|
|
25
|
+
constructor(config: StatelogConfig);
|
|
26
|
+
toJSON(): {
|
|
27
|
+
traceId: string;
|
|
28
|
+
projectId: string;
|
|
29
|
+
host: string;
|
|
30
|
+
debugMode: boolean;
|
|
31
|
+
};
|
|
32
|
+
debug(message: string, data: any): Promise<void>;
|
|
33
|
+
graph({ nodes, edges, startNode, }: {
|
|
34
|
+
nodes: string[];
|
|
35
|
+
edges: Record<string, JSONEdge>;
|
|
36
|
+
startNode?: string;
|
|
37
|
+
}): Promise<void>;
|
|
38
|
+
enterNode({ nodeId, data, }: {
|
|
39
|
+
nodeId: string;
|
|
40
|
+
data: any;
|
|
41
|
+
}): Promise<void>;
|
|
42
|
+
exitNode({ nodeId, data, timeTaken, }: {
|
|
43
|
+
nodeId: string;
|
|
44
|
+
data: any;
|
|
45
|
+
timeTaken?: number;
|
|
46
|
+
}): Promise<void>;
|
|
47
|
+
beforeHook({ nodeId, startData, endData, timeTaken, }: {
|
|
48
|
+
nodeId: string;
|
|
49
|
+
startData: any;
|
|
50
|
+
endData: any;
|
|
51
|
+
timeTaken?: number;
|
|
52
|
+
}): Promise<void>;
|
|
53
|
+
afterHook({ nodeId, startData, endData, timeTaken, }: {
|
|
54
|
+
nodeId: string;
|
|
55
|
+
startData: any;
|
|
56
|
+
endData: any;
|
|
57
|
+
timeTaken?: number;
|
|
58
|
+
}): Promise<void>;
|
|
59
|
+
followEdge({ fromNodeId, toNodeId, isConditionalEdge, data, }: {
|
|
60
|
+
fromNodeId: string;
|
|
61
|
+
toNodeId: string;
|
|
62
|
+
isConditionalEdge: boolean;
|
|
63
|
+
data: any;
|
|
64
|
+
}): Promise<void>;
|
|
65
|
+
promptCompletion({ messages, completion, model, timeTaken, tools, responseFormat, }: {
|
|
66
|
+
messages: any[];
|
|
67
|
+
completion: any;
|
|
68
|
+
model?: ModelName | ModelConfig | string;
|
|
69
|
+
timeTaken?: number;
|
|
70
|
+
tools?: {
|
|
71
|
+
name: string;
|
|
72
|
+
description?: string;
|
|
73
|
+
schema: any;
|
|
74
|
+
}[];
|
|
75
|
+
responseFormat?: any;
|
|
76
|
+
}): Promise<void>;
|
|
77
|
+
toolCall({ toolName, args, output, model, timeTaken, }: {
|
|
78
|
+
toolName: string;
|
|
79
|
+
args: any;
|
|
80
|
+
output: any;
|
|
81
|
+
model?: ModelName | ModelConfig;
|
|
82
|
+
timeTaken?: number;
|
|
83
|
+
}): Promise<void>;
|
|
84
|
+
diff({ itemA, itemB, message, }: {
|
|
85
|
+
itemA: any;
|
|
86
|
+
itemB: any;
|
|
87
|
+
message?: string;
|
|
88
|
+
}): Promise<void>;
|
|
89
|
+
upload({ projectId, entrypoint, files, }: {
|
|
90
|
+
projectId: string;
|
|
91
|
+
entrypoint: string;
|
|
92
|
+
files: AgencyFile[];
|
|
93
|
+
}): Promise<UploadResult>;
|
|
94
|
+
remoteRun({ files, entrypoint, args, }: {
|
|
95
|
+
files: AgencyFile[];
|
|
96
|
+
entrypoint: string;
|
|
97
|
+
args?: any[];
|
|
98
|
+
}): Promise<Result<any>>;
|
|
99
|
+
hitServer({ userId, projectId, filename, nodeName, body, }: {
|
|
100
|
+
userId: string;
|
|
101
|
+
projectId: string;
|
|
102
|
+
filename: string;
|
|
103
|
+
nodeName: string;
|
|
104
|
+
body: string;
|
|
105
|
+
}): Promise<Result<any>>;
|
|
106
|
+
post(body: Record<string, any>): Promise<void>;
|
|
107
|
+
}
|
|
108
|
+
export declare function getStatelogClient(config: {
|
|
109
|
+
host: string;
|
|
110
|
+
traceId?: string;
|
|
111
|
+
projectId: string;
|
|
112
|
+
debugMode?: boolean;
|
|
113
|
+
}): StatelogClient;
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import { nanoid } from "nanoid";
|
|
2
|
+
import { failure, mergeResults, success } from "./types/result.js";
|
|
3
|
+
export function mergeUploadResults(_results) {
|
|
4
|
+
const results = mergeResults(_results);
|
|
5
|
+
if (!results.success) {
|
|
6
|
+
return failure(results.error);
|
|
7
|
+
}
|
|
8
|
+
const endpointUrls = results.value.flatMap((r) => r.endpointUrls);
|
|
9
|
+
return success({
|
|
10
|
+
endpointUrls,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
export class StatelogClient {
|
|
14
|
+
host;
|
|
15
|
+
debugMode;
|
|
16
|
+
traceId;
|
|
17
|
+
apiKey;
|
|
18
|
+
projectId;
|
|
19
|
+
constructor(config) {
|
|
20
|
+
const { host, apiKey, projectId, traceId, debugMode } = config;
|
|
21
|
+
this.host = host;
|
|
22
|
+
this.apiKey = apiKey;
|
|
23
|
+
this.projectId = projectId;
|
|
24
|
+
this.debugMode = debugMode || false;
|
|
25
|
+
this.traceId = traceId || nanoid();
|
|
26
|
+
if (this.debugMode) {
|
|
27
|
+
console.log(`Statelog client initialized with host: ${host} and traceId: ${this.traceId}`, { config });
|
|
28
|
+
}
|
|
29
|
+
if (!this.apiKey) {
|
|
30
|
+
throw new Error("API key is required for StatelogClient");
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
toJSON() {
|
|
34
|
+
return {
|
|
35
|
+
traceId: this.traceId,
|
|
36
|
+
projectId: this.projectId,
|
|
37
|
+
host: this.host,
|
|
38
|
+
debugMode: this.debugMode,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
async debug(message, data) {
|
|
42
|
+
await this.post({
|
|
43
|
+
type: "debug",
|
|
44
|
+
message: message,
|
|
45
|
+
data,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
async graph({ nodes, edges, startNode, }) {
|
|
49
|
+
await this.post({
|
|
50
|
+
type: "graph",
|
|
51
|
+
nodes,
|
|
52
|
+
edges,
|
|
53
|
+
startNode,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
async enterNode({ nodeId, data, }) {
|
|
57
|
+
await this.post({
|
|
58
|
+
type: "enterNode",
|
|
59
|
+
nodeId,
|
|
60
|
+
data,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
async exitNode({ nodeId, data, timeTaken, }) {
|
|
64
|
+
await this.post({
|
|
65
|
+
type: "exitNode",
|
|
66
|
+
nodeId,
|
|
67
|
+
data,
|
|
68
|
+
timeTaken,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
async beforeHook({ nodeId, startData, endData, timeTaken, }) {
|
|
72
|
+
await this.post({
|
|
73
|
+
type: "beforeHook",
|
|
74
|
+
nodeId,
|
|
75
|
+
startData,
|
|
76
|
+
endData,
|
|
77
|
+
timeTaken,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
async afterHook({ nodeId, startData, endData, timeTaken, }) {
|
|
81
|
+
await this.post({
|
|
82
|
+
type: "afterHook",
|
|
83
|
+
nodeId,
|
|
84
|
+
startData,
|
|
85
|
+
endData,
|
|
86
|
+
timeTaken,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
async followEdge({ fromNodeId, toNodeId, isConditionalEdge, data, }) {
|
|
90
|
+
await this.post({
|
|
91
|
+
type: "followEdge",
|
|
92
|
+
edgeId: `${fromNodeId}->${toNodeId}`,
|
|
93
|
+
fromNodeId,
|
|
94
|
+
toNodeId,
|
|
95
|
+
isConditionalEdge,
|
|
96
|
+
data,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
async promptCompletion({ messages, completion, model, timeTaken, tools, responseFormat, }) {
|
|
100
|
+
await this.post({
|
|
101
|
+
type: "promptCompletion",
|
|
102
|
+
messages,
|
|
103
|
+
completion,
|
|
104
|
+
model,
|
|
105
|
+
timeTaken,
|
|
106
|
+
tools,
|
|
107
|
+
responseFormat,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
async toolCall({ toolName, args, output, model, timeTaken, }) {
|
|
111
|
+
await this.post({
|
|
112
|
+
type: "toolCall",
|
|
113
|
+
toolName,
|
|
114
|
+
args,
|
|
115
|
+
output,
|
|
116
|
+
model,
|
|
117
|
+
timeTaken,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
async diff({ itemA, itemB, message, }) {
|
|
121
|
+
await this.post({
|
|
122
|
+
type: "diff",
|
|
123
|
+
itemA,
|
|
124
|
+
itemB,
|
|
125
|
+
message,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
/* async promptResult({ result }: { result: PromptResult }): Promise<void> {
|
|
129
|
+
await this.post({
|
|
130
|
+
type: "promptResult",
|
|
131
|
+
result,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
*/
|
|
135
|
+
async upload({ projectId, entrypoint, files, }) {
|
|
136
|
+
try {
|
|
137
|
+
const fullUrl = new URL(`/api/projects/${projectId}/upload`, this.host);
|
|
138
|
+
const url = fullUrl.toString();
|
|
139
|
+
const postBody = JSON.stringify({ entrypoint, files });
|
|
140
|
+
console.log({ entrypoint, files }, postBody);
|
|
141
|
+
const result = await fetch(url, {
|
|
142
|
+
method: "POST",
|
|
143
|
+
headers: {
|
|
144
|
+
"Content-Type": "application/json",
|
|
145
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
146
|
+
},
|
|
147
|
+
body: postBody,
|
|
148
|
+
}).catch((err) => {
|
|
149
|
+
if (this.debugMode)
|
|
150
|
+
console.error("Failed to send statelog:", err);
|
|
151
|
+
});
|
|
152
|
+
if (result) {
|
|
153
|
+
if (!result.ok) {
|
|
154
|
+
if (this.debugMode)
|
|
155
|
+
console.error("Failed to upload files to statelog:", {
|
|
156
|
+
result,
|
|
157
|
+
url,
|
|
158
|
+
files,
|
|
159
|
+
});
|
|
160
|
+
return failure("Failed to upload files to statelog");
|
|
161
|
+
}
|
|
162
|
+
return (await result.json());
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
catch (err) {
|
|
166
|
+
if (this.debugMode)
|
|
167
|
+
console.error("Error sending log in statelog client:", err, {
|
|
168
|
+
host: this.host,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
return failure("Error uploading files to statelog");
|
|
172
|
+
}
|
|
173
|
+
async remoteRun({ files, entrypoint, args, }) {
|
|
174
|
+
try {
|
|
175
|
+
const fullUrl = new URL(`/api/run`, this.host);
|
|
176
|
+
const url = fullUrl.toString();
|
|
177
|
+
const body = JSON.stringify({
|
|
178
|
+
files,
|
|
179
|
+
entrypoint,
|
|
180
|
+
args,
|
|
181
|
+
});
|
|
182
|
+
console.log({ entrypoint, args }, body);
|
|
183
|
+
const result = await fetch(url, {
|
|
184
|
+
method: "POST",
|
|
185
|
+
headers: {
|
|
186
|
+
"Content-Type": "application/json",
|
|
187
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
188
|
+
},
|
|
189
|
+
body,
|
|
190
|
+
}).catch((err) => {
|
|
191
|
+
if (this.debugMode)
|
|
192
|
+
console.error("Failed to run on statelog:", err);
|
|
193
|
+
});
|
|
194
|
+
if (result) {
|
|
195
|
+
if (!result.ok) {
|
|
196
|
+
if (this.debugMode) {
|
|
197
|
+
const responseBody = await result.text();
|
|
198
|
+
console.error("Failed to run on statelog:", {
|
|
199
|
+
result,
|
|
200
|
+
url,
|
|
201
|
+
body,
|
|
202
|
+
responseBody,
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
return failure("Failed to run on statelog");
|
|
206
|
+
}
|
|
207
|
+
return (await result.json());
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
catch (err) {
|
|
211
|
+
if (this.debugMode)
|
|
212
|
+
console.error("Error running on statelog client:", err, {
|
|
213
|
+
host: this.host,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
return failure("Error running on statelog");
|
|
217
|
+
}
|
|
218
|
+
async hitServer({ userId, projectId, filename, nodeName, body, }) {
|
|
219
|
+
try {
|
|
220
|
+
const fullUrl = new URL(`/run/${userId}/${projectId}/${filename}/${nodeName}`, this.host);
|
|
221
|
+
const url = fullUrl.toString();
|
|
222
|
+
const result = await fetch(url, {
|
|
223
|
+
method: "POST",
|
|
224
|
+
headers: {
|
|
225
|
+
"Content-Type": "application/json",
|
|
226
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
227
|
+
},
|
|
228
|
+
body: body,
|
|
229
|
+
}).catch((err) => {
|
|
230
|
+
if (this.debugMode)
|
|
231
|
+
console.error("Failed to run on statelog:", err);
|
|
232
|
+
});
|
|
233
|
+
if (result) {
|
|
234
|
+
if (!result.ok) {
|
|
235
|
+
if (this.debugMode) {
|
|
236
|
+
const responseBody = await result.text();
|
|
237
|
+
console.error("Failed to run on statelog:", {
|
|
238
|
+
result,
|
|
239
|
+
url,
|
|
240
|
+
body,
|
|
241
|
+
responseBody,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
return failure("Failed to run on statelog");
|
|
245
|
+
}
|
|
246
|
+
return (await result.json());
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
catch (err) {
|
|
250
|
+
if (this.debugMode)
|
|
251
|
+
console.error("Error running on statelog client:", err, {
|
|
252
|
+
host: this.host,
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
return failure("Error running on statelog");
|
|
256
|
+
}
|
|
257
|
+
async post(body) {
|
|
258
|
+
if (!this.host) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
const postBody = JSON.stringify({
|
|
262
|
+
trace_id: this.traceId,
|
|
263
|
+
project_id: this.projectId,
|
|
264
|
+
data: { ...body, timestamp: new Date().toISOString() },
|
|
265
|
+
});
|
|
266
|
+
if (this.host.toLowerCase() === "stdout") {
|
|
267
|
+
console.log(postBody);
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
try {
|
|
271
|
+
const fullUrl = new URL("/api/logs", this.host);
|
|
272
|
+
const url = fullUrl.toString();
|
|
273
|
+
await fetch(url, {
|
|
274
|
+
method: "POST",
|
|
275
|
+
headers: {
|
|
276
|
+
"Content-Type": "application/json",
|
|
277
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
278
|
+
},
|
|
279
|
+
body: postBody,
|
|
280
|
+
}).catch((err) => {
|
|
281
|
+
if (this.debugMode)
|
|
282
|
+
console.error("Failed to send statelog:", err);
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
catch (err) {
|
|
286
|
+
if (this.debugMode)
|
|
287
|
+
console.error("Error sending log in statelog client:", err, {
|
|
288
|
+
host: this.host,
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
export function getStatelogClient(config) {
|
|
294
|
+
const statelogConfig = {
|
|
295
|
+
host: config.host,
|
|
296
|
+
traceId: config.traceId || nanoid(),
|
|
297
|
+
apiKey: process.env.STATELOG_API_KEY || "",
|
|
298
|
+
projectId: config.projectId,
|
|
299
|
+
debugMode: config.debugMode || false,
|
|
300
|
+
};
|
|
301
|
+
const client = new StatelogClient(statelogConfig);
|
|
302
|
+
return client;
|
|
303
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -5,6 +5,10 @@ import { Message } from "./classes/message/index.js";
|
|
|
5
5
|
import { ToolCall } from "./classes/ToolCall.js";
|
|
6
6
|
import { ModelConfig, ModelName, Provider } from "./models.js";
|
|
7
7
|
import { Result } from "./types/result.js";
|
|
8
|
+
export type ThinkingBlock = {
|
|
9
|
+
text: string;
|
|
10
|
+
signature: string;
|
|
11
|
+
};
|
|
8
12
|
export type PromptConfig = {
|
|
9
13
|
messages: Message[];
|
|
10
14
|
tools?: {
|
|
@@ -19,6 +23,10 @@ export type PromptConfig = {
|
|
|
19
23
|
parallelToolCalls?: boolean;
|
|
20
24
|
responseFormat?: ZodType;
|
|
21
25
|
stream?: boolean;
|
|
26
|
+
thinking?: {
|
|
27
|
+
enabled: boolean;
|
|
28
|
+
budgetTokens?: number;
|
|
29
|
+
};
|
|
22
30
|
responseFormatOptions?: Partial<{
|
|
23
31
|
name: string;
|
|
24
32
|
strict: boolean;
|
|
@@ -38,6 +46,12 @@ export type SmolConfig = {
|
|
|
38
46
|
provider?: Provider;
|
|
39
47
|
logLevel?: LogLevel;
|
|
40
48
|
toolLoopDetection?: ToolLoopDetection;
|
|
49
|
+
statelog?: Partial<{
|
|
50
|
+
host: string;
|
|
51
|
+
projectId: string;
|
|
52
|
+
debugMode: boolean;
|
|
53
|
+
apiKey: string;
|
|
54
|
+
}>;
|
|
41
55
|
};
|
|
42
56
|
export type ToolLoopDetection = {
|
|
43
57
|
enabled: boolean;
|
|
@@ -65,6 +79,7 @@ export type CostEstimate = {
|
|
|
65
79
|
export type PromptResult = {
|
|
66
80
|
output: string | null;
|
|
67
81
|
toolCalls: ToolCall[];
|
|
82
|
+
thinkingBlocks?: ThinkingBlock[];
|
|
68
83
|
usage?: TokenUsage;
|
|
69
84
|
cost?: CostEstimate;
|
|
70
85
|
model?: ModelName | ModelConfig;
|
|
@@ -72,6 +87,10 @@ export type PromptResult = {
|
|
|
72
87
|
export type StreamChunk = {
|
|
73
88
|
type: "text";
|
|
74
89
|
text: string;
|
|
90
|
+
} | {
|
|
91
|
+
type: "thinking";
|
|
92
|
+
text: string;
|
|
93
|
+
signature?: string;
|
|
75
94
|
} | {
|
|
76
95
|
type: "tool_call";
|
|
77
96
|
toolCall: ToolCall;
|
|
@@ -95,3 +114,10 @@ export type TextPart = {
|
|
|
95
114
|
type: "text";
|
|
96
115
|
text: string;
|
|
97
116
|
};
|
|
117
|
+
export type JSONEdge = {
|
|
118
|
+
type: "regular";
|
|
119
|
+
to: string;
|
|
120
|
+
} | {
|
|
121
|
+
type: "conditional";
|
|
122
|
+
adjacentNodes: readonly string[];
|
|
123
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "smoltalk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.36",
|
|
4
4
|
"description": "A common interface for LLM APIs",
|
|
5
5
|
"homepage": "https://github.com/egonSchiele/smoltalk",
|
|
6
6
|
"scripts": {
|
|
@@ -44,7 +44,8 @@
|
|
|
44
44
|
"@anthropic-ai/sdk": "^0.78.0",
|
|
45
45
|
"@google/genai": "^1.34.0",
|
|
46
46
|
"egonlog": "^0.0.2",
|
|
47
|
+
"nanoid": "^5.1.6",
|
|
47
48
|
"ollama": "^0.6.3",
|
|
48
49
|
"openai": "^6.15.0"
|
|
49
50
|
}
|
|
50
|
-
}
|
|
51
|
+
}
|