smoltalk 0.0.1 → 0.0.3
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 +16 -212
- package/dist/{lib/clients → clients}/google.js +1 -1
- package/dist/clients/openai.js +45 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.js +5 -11
- package/dist/{lib/models.d.ts → models.d.ts} +1 -1
- package/dist/{lib/types.d.ts → types.d.ts} +10 -2
- package/dist/util/openai.d.ts +4 -0
- package/dist/util/openai.js +10 -0
- package/dist/util.d.ts +1 -0
- package/dist/util.js +1 -0
- package/package.json +1 -1
- package/dist/lib/clients/openai.js +0 -28
- package/dist/lib/index.d.ts +0 -4
- package/dist/lib/index.js +0 -4
- package/dist/{lib/client.d.ts → client.d.ts} +0 -0
- package/dist/{lib/client.js → client.js} +1 -1
- /package/dist/{lib/clients → clients}/google.d.ts +0 -0
- /package/dist/{lib/clients → clients}/openai.d.ts +0 -0
- /package/dist/{lib/models.js → models.js} +0 -0
- /package/dist/{lib/smolError.d.ts → smolError.d.ts} +0 -0
- /package/dist/{lib/smolError.js → smolError.js} +0 -0
- /package/dist/{lib/types → types}/result.d.ts +0 -0
- /package/dist/{lib/types → types}/result.js +0 -0
- /package/dist/{lib/types.js → types.js} +0 -0
package/README.md
CHANGED
|
@@ -1,222 +1,26 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Smoltalk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## Install
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## Quickstart
|
|
8
|
-
Install Typestache:
|
|
9
|
-
|
|
10
|
-
```bash
|
|
11
|
-
npm install typestache
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
Typestache consists of a CLI tool and a library. To use it, point the CLI tool towards your template directory:
|
|
15
|
-
|
|
16
|
-
```bash
|
|
17
|
-
typestache src/templates
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
Typestache will find your mustache files, and create a corresponding TypeScript file:
|
|
21
|
-
|
|
22
|
-
```bash
|
|
23
|
-
src/templates
|
|
24
|
-
- myTemplate.mustache
|
|
25
|
-
- myTemplate.ts
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
Now simply import this TypeScript file and render it.
|
|
29
|
-
|
|
30
|
-
```typescript
|
|
31
|
-
import myTemplate from './myTemplate';
|
|
32
|
-
|
|
33
|
-
const data = {
|
|
34
|
-
name: 'Adit',
|
|
35
|
-
value: 10000,
|
|
36
|
-
in_ca: true
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const result = myTemplate(data);
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
Easy as that! Behind the scenes, Typestache has converted your mustache template into a typed template for you, so if you have a type error, TypeScript will tell you. Example:
|
|
43
|
-
|
|
44
|
-
```typescript
|
|
45
|
-
const data = {
|
|
46
|
-
name: 'Adit',
|
|
47
|
-
value: 10000,
|
|
48
|
-
in_ca: "true" // Oops, this should be a boolean
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
const result = myTemplate(data); // Error: Type 'string' is not assignable to type 'boolean'.
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
[See examples here](https://github.com/egonSchiele/typestache/tree/main/examples).
|
|
55
|
-
|
|
56
|
-
Typestache also extends mustache syntax to add type hints. Here's a short example:
|
|
57
|
-
|
|
58
|
-
```mustache
|
|
59
|
-
I am {{age:number}} years old.
|
|
5
|
+
```bash
|
|
6
|
+
pnpm install smoltalk
|
|
60
7
|
```
|
|
61
8
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
**Heads up, typestache is *not* a drop-in replacement for mustache.** Read more below.
|
|
65
|
-
|
|
66
|
-
## Deriving types
|
|
67
|
-
|
|
68
|
-
Typestache will automatically derive types for you. For example, given this template
|
|
69
|
-
|
|
70
|
-
```mustache
|
|
71
|
-
{{#person}}
|
|
72
|
-
Hello, {{name}}!
|
|
73
|
-
{{/person}}
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
Typestache will derive this type:
|
|
9
|
+
## Usage
|
|
77
10
|
|
|
78
11
|
```typescript
|
|
79
|
-
|
|
80
|
-
person: boolean;
|
|
81
|
-
name: string | boolean | number;
|
|
82
|
-
};
|
|
83
|
-
```
|
|
12
|
+
import { getClient } from "smoltalk";
|
|
84
13
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
Hello, {{name:string}}!
|
|
91
|
-
{{/person}}
|
|
92
|
-
```
|
|
14
|
+
const client = getClient({
|
|
15
|
+
apiKey: process.env.GEMINI_API_KEY || "",
|
|
16
|
+
logLevel: "debug",
|
|
17
|
+
model: "gemini-2.0-flash-lite",
|
|
18
|
+
});
|
|
93
19
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
type TemplateType = {
|
|
98
|
-
person: boolean;
|
|
99
|
-
name: string;
|
|
100
|
-
};
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
Here is another example. `amount` can be a `string` or a `number`, so we have used a union here.
|
|
104
|
-
|
|
105
|
-
```mustache
|
|
106
|
-
{{#person}}
|
|
107
|
-
Hello, {{name:string}}! You have {{amount:string|number}} in your account.
|
|
108
|
-
{{/person}}
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
and here's the derived type:
|
|
112
|
-
|
|
113
|
-
```typescript
|
|
114
|
-
type TemplateType = {
|
|
115
|
-
person: boolean;
|
|
116
|
-
name: string;
|
|
117
|
-
amount: string | number;
|
|
118
|
-
};
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
### Sections and scoping
|
|
122
|
-
|
|
123
|
-
In all these examples, you'll notice `name` is never a key. `person` is always a `boolean`, it's never an object with a key `name`. Mustache has very loose scoping rules. Deriving a type for this template
|
|
124
|
-
|
|
125
|
-
```mustache
|
|
126
|
-
{{#person}}
|
|
127
|
-
Hello, {{name}}!
|
|
128
|
-
{{/person}}
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
in mustache might look something like this:
|
|
132
|
-
|
|
133
|
-
```typescript
|
|
134
|
-
type TemplateType = {
|
|
135
|
-
person: boolean;
|
|
136
|
-
name: string | boolean | number;
|
|
137
|
-
} | {
|
|
138
|
-
person: {
|
|
139
|
-
name: string | boolean | number;
|
|
140
|
-
}
|
|
141
|
-
} | {
|
|
142
|
-
person: {
|
|
143
|
-
name: string | boolean | number;
|
|
144
|
-
}
|
|
145
|
-
}[]
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
Even that's not enough, since technically, `person` could be any truthy value, and `person` and `name` could both be `undefined`.
|
|
149
|
-
|
|
150
|
-
A type like this is harder to read, and reduces type safety. Things look even worse as you have more sections, and more variables. So typestache chooses to interpret every variable as if it's in the global context. If you want `name` to be a key on `person`, use the new `this` keyword:
|
|
151
|
-
|
|
152
|
-
```mustache
|
|
153
|
-
{{#person}}
|
|
154
|
-
Hello, {{this.name}}!
|
|
155
|
-
{{/person}}
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
Generates this type:
|
|
159
|
-
|
|
160
|
-
```typescript
|
|
161
|
-
type TemplateType = {
|
|
162
|
-
person: {
|
|
163
|
-
name: string | boolean | number;
|
|
164
|
-
}
|
|
20
|
+
async function main() {
|
|
21
|
+
const resp = await client.text("Hello, how are you?");
|
|
22
|
+
console.log(resp);
|
|
165
23
|
}
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
You'll also notice `person` is an object. If you want it to be an array of objects, use `[]` after the name in the opening tag:
|
|
169
|
-
|
|
170
|
-
```mustache
|
|
171
|
-
{{#person[]}}
|
|
172
|
-
Hello, {{this.name}}!
|
|
173
|
-
{{/person}}
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
Generates this type:
|
|
178
|
-
|
|
179
|
-
```typescript
|
|
180
|
-
type TemplateType = {
|
|
181
|
-
person: {
|
|
182
|
-
name: string | boolean | number;
|
|
183
|
-
}[];
|
|
184
|
-
}
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
### Optionals
|
|
188
|
-
|
|
189
|
-
Finally, typestache makes all variables required by default. You can make something optional by adding a question mark at the end of the name, like this:
|
|
190
|
-
|
|
191
|
-
```mustache
|
|
192
|
-
Hello, {{name?:string}}!
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
Generates this type:
|
|
196
|
-
|
|
197
|
-
```typescript
|
|
198
|
-
type TemplateType = {
|
|
199
|
-
name?: string;
|
|
200
|
-
}
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
## Typestache doesn't implement the entire mustache spec.
|
|
204
|
-
|
|
205
|
-
There are several parts of the mustache spec that Typestache does not implement. The most important one to know about is that Typestache handles scope differently. Mustache is very loose with its scoping, which makes it hard to write a useful type for it.
|
|
206
|
-
|
|
207
|
-
Here are some other things not currently supported:
|
|
208
|
-
|
|
209
|
-
Eventual support:
|
|
210
|
-
|
|
211
|
-
- Nested sections
|
|
212
|
-
- Lambdas (no support for dynamic templates)
|
|
213
|
-
- partials
|
|
214
|
-
|
|
215
|
-
No support planned:
|
|
216
|
-
|
|
217
|
-
- Dynamic names
|
|
218
|
-
- blocks
|
|
219
|
-
- parents
|
|
220
|
-
- custom delimiter tags.
|
|
221
24
|
|
|
222
|
-
|
|
25
|
+
main();
|
|
26
|
+
```
|
|
@@ -34,6 +34,6 @@ export class SmolGoogle {
|
|
|
34
34
|
//console.log("Full response:", JSON.stringify(result, null, 2));
|
|
35
35
|
const text = result.text;
|
|
36
36
|
// Return the response, updating the chat history
|
|
37
|
-
return success({ output: result.text });
|
|
37
|
+
return success({ output: result.text, toolCalls: [] });
|
|
38
38
|
}
|
|
39
39
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import OpenAI from "openai";
|
|
2
|
+
import { success, } from "../types.js";
|
|
3
|
+
import { isFunctionToolCall, openAIToToolCall } from "../util.js";
|
|
4
|
+
export class SmolOpenAi {
|
|
5
|
+
client;
|
|
6
|
+
logger;
|
|
7
|
+
model;
|
|
8
|
+
constructor(config) {
|
|
9
|
+
this.client = new OpenAI({ apiKey: config.apiKey });
|
|
10
|
+
this.logger = config.logger;
|
|
11
|
+
this.model = config.model;
|
|
12
|
+
}
|
|
13
|
+
getClient() {
|
|
14
|
+
return this.client;
|
|
15
|
+
}
|
|
16
|
+
async text(content, config) {
|
|
17
|
+
const messages = structuredClone(config?.messages) || [];
|
|
18
|
+
messages.push({ role: "user", content });
|
|
19
|
+
const completion = await this.client.chat.completions.create({
|
|
20
|
+
model: this.model,
|
|
21
|
+
messages,
|
|
22
|
+
tools: config?.tools,
|
|
23
|
+
response_format: config?.responseFormat,
|
|
24
|
+
});
|
|
25
|
+
this.logger.debug("Response from OpenAI:", JSON.stringify(completion, null, 2));
|
|
26
|
+
const message = completion.choices[0].message;
|
|
27
|
+
const output = message.content;
|
|
28
|
+
const _toolCalls = message.tool_calls;
|
|
29
|
+
const toolCalls = [];
|
|
30
|
+
if (_toolCalls) {
|
|
31
|
+
for (const tc of _toolCalls) {
|
|
32
|
+
if (isFunctionToolCall(tc)) {
|
|
33
|
+
toolCalls.push(openAIToToolCall(tc));
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
this.logger.warn(`Unsupported tool call type: ${tc.type} for tool call ID: ${tc.id}`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (toolCalls.length > 0) {
|
|
41
|
+
this.logger.info("Tool calls detected:", toolCalls);
|
|
42
|
+
}
|
|
43
|
+
return success({ output, toolCalls });
|
|
44
|
+
}
|
|
45
|
+
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
});
|
|
7
|
-
async function main() {
|
|
8
|
-
const resp = await client.text("Hello, how are you?");
|
|
9
|
-
console.log(resp);
|
|
10
|
-
}
|
|
11
|
-
main();
|
|
1
|
+
export * from "./client.js";
|
|
2
|
+
export * from "./types.js";
|
|
3
|
+
export * from "./models.js";
|
|
4
|
+
export * from "./smolError.js";
|
|
5
|
+
export * from "./util.js";
|
|
@@ -247,7 +247,7 @@ export type TextModelName = (typeof textModels)[number]["modelName"];
|
|
|
247
247
|
export type ImageModelName = (typeof imageModels)[number]["modelName"];
|
|
248
248
|
export type SpeechToTextModelName = (typeof speechToTextModels)[number]["modelName"];
|
|
249
249
|
export type EmbeddingsModelName = (typeof embeddingsModels)[number]["modelName"];
|
|
250
|
-
export type ModelName = TextModelName | ImageModelName | SpeechToTextModelName
|
|
250
|
+
export type ModelName = TextModelName | ImageModelName | SpeechToTextModelName;
|
|
251
251
|
export declare function getModel(modelName: ModelName): {
|
|
252
252
|
readonly type: "speech-to-text";
|
|
253
253
|
readonly modelName: "whisper-local";
|
|
@@ -3,16 +3,18 @@ import { EgonLog, LogLevel } from "egonlog";
|
|
|
3
3
|
import { ModelName } from "./models.js";
|
|
4
4
|
import { Result } from "./index.js";
|
|
5
5
|
export type PromptConfig<Tool = any> = {
|
|
6
|
+
tools?: Tool[];
|
|
6
7
|
messages?: {
|
|
7
8
|
role: string;
|
|
8
9
|
content: string;
|
|
10
|
+
name?: string;
|
|
9
11
|
}[];
|
|
10
12
|
instructions?: string;
|
|
11
13
|
maxTokens?: number;
|
|
12
14
|
temperature?: number;
|
|
13
15
|
numSuggestions?: number;
|
|
14
16
|
parallelToolCalls?: boolean;
|
|
15
|
-
|
|
17
|
+
responseFormat?: any;
|
|
16
18
|
};
|
|
17
19
|
export type SmolConfig = {
|
|
18
20
|
apiKey: string;
|
|
@@ -22,8 +24,14 @@ export type SmolConfig = {
|
|
|
22
24
|
export type BaseClientConfig = SmolConfig & {
|
|
23
25
|
logger: EgonLog;
|
|
24
26
|
};
|
|
27
|
+
export type ToolCall = {
|
|
28
|
+
id: string;
|
|
29
|
+
name: string;
|
|
30
|
+
arguments: Record<string, any>;
|
|
31
|
+
};
|
|
25
32
|
export type PromptResult = {
|
|
26
|
-
output: string;
|
|
33
|
+
output: string | null;
|
|
34
|
+
toolCalls: ToolCall[];
|
|
27
35
|
};
|
|
28
36
|
export interface SmolClient {
|
|
29
37
|
text(content: string, config?: PromptConfig): Promise<Result<PromptResult>>;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ChatCompletionMessageFunctionToolCall, ChatCompletionMessageToolCall } from "openai/resources";
|
|
2
|
+
import { ToolCall } from "../types.js";
|
|
3
|
+
export declare function openAIToToolCall(toolCall: ChatCompletionMessageFunctionToolCall): ToolCall;
|
|
4
|
+
export declare function isFunctionToolCall(message: ChatCompletionMessageToolCall): message is ChatCompletionMessageFunctionToolCall;
|
package/dist/util.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./util/openai.js";
|
package/dist/util.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./util/openai.js";
|
package/package.json
CHANGED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import OpenAI from "openai";
|
|
2
|
-
import { success, } from "../types.js";
|
|
3
|
-
export class SmolOpenAi {
|
|
4
|
-
client;
|
|
5
|
-
logger;
|
|
6
|
-
model;
|
|
7
|
-
constructor(config) {
|
|
8
|
-
this.client = new OpenAI({ apiKey: config.apiKey });
|
|
9
|
-
this.logger = config.logger;
|
|
10
|
-
this.model = config.model;
|
|
11
|
-
}
|
|
12
|
-
getClient() {
|
|
13
|
-
return this.client;
|
|
14
|
-
}
|
|
15
|
-
async text(content, config) {
|
|
16
|
-
const messages = structuredClone(config?.messages) || [];
|
|
17
|
-
messages.push({ role: "user", content });
|
|
18
|
-
const response = await this.client.responses.create({
|
|
19
|
-
model: this.model,
|
|
20
|
-
instructions: config?.instructions,
|
|
21
|
-
input: content,
|
|
22
|
-
max_output_tokens: config?.maxTokens,
|
|
23
|
-
temperature: config?.temperature,
|
|
24
|
-
parallel_tool_calls: config?.parallelToolCalls,
|
|
25
|
-
});
|
|
26
|
-
return success({ output: response.output_text });
|
|
27
|
-
}
|
|
28
|
-
}
|
package/dist/lib/index.d.ts
DELETED
package/dist/lib/index.js
DELETED
|
File without changes
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
export * from "./clients/google.js";
|
|
2
2
|
export * from "./clients/openai.js";
|
|
3
|
+
import { EgonLog } from "egonlog";
|
|
3
4
|
import { SmolGoogle } from "./clients/google.js";
|
|
4
5
|
import { SmolOpenAi } from "./clients/openai.js";
|
|
5
6
|
import { getModel, isTextModel } from "./models.js";
|
|
6
7
|
import { SmolError } from "./smolError.js";
|
|
7
|
-
import { EgonLog } from "egonlog";
|
|
8
8
|
export function getClient(config) {
|
|
9
9
|
const apiKey = config.apiKey;
|
|
10
10
|
const logger = new EgonLog({ level: config.logLevel || "info" });
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|