smoltalk 0.2.2 → 0.3.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/dist/client.js +9 -10
- package/dist/clients/baseClient.js +2 -4
- package/dist/embed/google.d.ts +3 -0
- package/dist/embed/google.js +25 -0
- package/dist/embed/ollama.d.ts +3 -0
- package/dist/embed/ollama.js +35 -0
- package/dist/embed/openai.d.ts +3 -0
- package/dist/embed/openai.js +42 -0
- package/dist/embed.d.ts +21 -0
- package/dist/embed.js +35 -0
- package/dist/image/google.d.ts +3 -0
- package/dist/image/google.js +57 -0
- package/dist/image/openai.d.ts +3 -0
- package/dist/image/openai.js +140 -0
- package/dist/image.d.ts +35 -0
- package/dist/image.js +37 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/models.d.ts +231 -26
- package/dist/models.js +147 -12
- package/dist/util/imageRef.d.ts +29 -0
- package/dist/util/imageRef.js +51 -0
- package/dist/util/provider.d.ts +17 -0
- package/dist/util/provider.js +34 -0
- package/dist/util/util.d.ts +23 -0
- package/dist/util/util.js +40 -0
- package/package.json +2 -2
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export type ImageRef = {
|
|
2
|
+
kind: "bytes";
|
|
3
|
+
data: Uint8Array;
|
|
4
|
+
mimeType: string;
|
|
5
|
+
} | {
|
|
6
|
+
kind: "base64";
|
|
7
|
+
base64: string;
|
|
8
|
+
mimeType: string;
|
|
9
|
+
} | {
|
|
10
|
+
kind: "path";
|
|
11
|
+
path: string;
|
|
12
|
+
mimeType?: string;
|
|
13
|
+
} | {
|
|
14
|
+
kind: "url";
|
|
15
|
+
url: string;
|
|
16
|
+
mimeType?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Maximum time in milliseconds to wait for the URL to respond before
|
|
19
|
+
* aborting. Defaults to {@link DEFAULT_FETCH_TIMEOUT_MS} (60 seconds).
|
|
20
|
+
*/
|
|
21
|
+
timeoutMs?: number;
|
|
22
|
+
};
|
|
23
|
+
export type NormalizedImage = {
|
|
24
|
+
data: Uint8Array;
|
|
25
|
+
mimeType: string;
|
|
26
|
+
};
|
|
27
|
+
/** Default timeout for fetching image URLs during normalization (60 seconds). */
|
|
28
|
+
export declare const DEFAULT_FETCH_TIMEOUT_MS = 60000;
|
|
29
|
+
export declare function normalizeImageRef(ref: ImageRef): Promise<NormalizedImage>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { extname } from "node:path";
|
|
3
|
+
/** Default timeout for fetching image URLs during normalization (60 seconds). */
|
|
4
|
+
export const DEFAULT_FETCH_TIMEOUT_MS = 60_000;
|
|
5
|
+
const EXT_TO_MIME = {
|
|
6
|
+
".png": "image/png",
|
|
7
|
+
".jpg": "image/jpeg",
|
|
8
|
+
".jpeg": "image/jpeg",
|
|
9
|
+
".webp": "image/webp",
|
|
10
|
+
".gif": "image/gif",
|
|
11
|
+
};
|
|
12
|
+
export async function normalizeImageRef(ref) {
|
|
13
|
+
switch (ref.kind) {
|
|
14
|
+
case "bytes":
|
|
15
|
+
return { data: ref.data, mimeType: ref.mimeType };
|
|
16
|
+
case "base64":
|
|
17
|
+
return {
|
|
18
|
+
data: new Uint8Array(Buffer.from(ref.base64, "base64")),
|
|
19
|
+
mimeType: ref.mimeType,
|
|
20
|
+
};
|
|
21
|
+
case "path": {
|
|
22
|
+
const buf = await readFile(ref.path);
|
|
23
|
+
const ext = extname(ref.path).toLowerCase();
|
|
24
|
+
const inferred = EXT_TO_MIME[ext];
|
|
25
|
+
const mimeType = ref.mimeType ?? inferred;
|
|
26
|
+
if (!mimeType || !mimeType.startsWith("image/")) {
|
|
27
|
+
throw new Error(`Could not determine image MIME type for path "${ref.path}". ` +
|
|
28
|
+
`Pass an explicit mimeType (e.g. "image/png") on the ImageRef.`);
|
|
29
|
+
}
|
|
30
|
+
return { data: new Uint8Array(buf), mimeType };
|
|
31
|
+
}
|
|
32
|
+
case "url": {
|
|
33
|
+
const timeoutMs = ref.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;
|
|
34
|
+
const res = await fetch(ref.url, {
|
|
35
|
+
signal: AbortSignal.timeout(timeoutMs),
|
|
36
|
+
});
|
|
37
|
+
if (!res.ok) {
|
|
38
|
+
throw new Error(`Failed to fetch image from ${ref.url}: ${res.status}`);
|
|
39
|
+
}
|
|
40
|
+
const buf = new Uint8Array(await res.arrayBuffer());
|
|
41
|
+
const contentType = res.headers.get("content-type") ?? undefined;
|
|
42
|
+
const mimeType = ref.mimeType ?? contentType;
|
|
43
|
+
if (!mimeType || !mimeType.startsWith("image/")) {
|
|
44
|
+
throw new Error(`Could not determine image MIME type for URL "${ref.url}". ` +
|
|
45
|
+
`Response Content-Type was "${contentType ?? "missing"}". ` +
|
|
46
|
+
`Pass an explicit mimeType (e.g. "image/png") on the ImageRef.`);
|
|
47
|
+
}
|
|
48
|
+
return { data: buf, mimeType };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the provider for a given model name.
|
|
3
|
+
* If an explicit provider is given, returns it directly.
|
|
4
|
+
* Otherwise looks up the model in the registry.
|
|
5
|
+
*/
|
|
6
|
+
export declare function resolveProvider(modelName: string, explicitProvider?: string): string;
|
|
7
|
+
type ApiKeyConfig = {
|
|
8
|
+
openAiApiKey?: string;
|
|
9
|
+
googleApiKey?: string;
|
|
10
|
+
anthropicApiKey?: string;
|
|
11
|
+
ollamaApiKey?: string;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Resolve the API key for a provider, checking config then env vars.
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolveApiKey(provider: string, config: ApiKeyConfig): string | undefined;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { getModel } from "../models.js";
|
|
2
|
+
import { SmolError } from "../smolError.js";
|
|
3
|
+
/**
|
|
4
|
+
* Resolve the provider for a given model name.
|
|
5
|
+
* If an explicit provider is given, returns it directly.
|
|
6
|
+
* Otherwise looks up the model in the registry.
|
|
7
|
+
*/
|
|
8
|
+
export function resolveProvider(modelName, explicitProvider) {
|
|
9
|
+
if (explicitProvider)
|
|
10
|
+
return explicitProvider;
|
|
11
|
+
const model = getModel(modelName);
|
|
12
|
+
if (model === undefined) {
|
|
13
|
+
throw new SmolError(`Model ${modelName} is not recognized. Please specify a known model, or explicitly set the provider option in the config.`);
|
|
14
|
+
}
|
|
15
|
+
return model.provider;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Resolve the API key for a provider, checking config then env vars.
|
|
19
|
+
*/
|
|
20
|
+
export function resolveApiKey(provider, config) {
|
|
21
|
+
switch (provider) {
|
|
22
|
+
case "openai":
|
|
23
|
+
case "openai-responses":
|
|
24
|
+
return config.openAiApiKey || process.env.OPENAI_API_KEY;
|
|
25
|
+
case "google":
|
|
26
|
+
return config.googleApiKey || process.env.GEMINI_API_KEY;
|
|
27
|
+
case "anthropic":
|
|
28
|
+
return config.anthropicApiKey || process.env.ANTHROPIC_API_KEY;
|
|
29
|
+
case "ollama":
|
|
30
|
+
return config.ollamaApiKey;
|
|
31
|
+
default:
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
}
|
package/dist/util/util.d.ts
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
export * from "./openai.js";
|
|
2
2
|
export declare function round(num: number, places: number): number;
|
|
3
|
+
/** Token-cost prices in the model registry are quoted per 1M tokens. */
|
|
4
|
+
export declare const TOKENS_PER_MILLION = 1000000;
|
|
5
|
+
/** Round all dollar costs to 6 decimal places (1/100 of a cent). */
|
|
6
|
+
export declare const COST_DECIMAL_PLACES = 6;
|
|
7
|
+
/**
|
|
8
|
+
* Compute the dollar cost of `tokens` at `costPerMillion` (USD per 1M tokens).
|
|
9
|
+
* Returns 0 if either argument is missing or non-positive.
|
|
10
|
+
*/
|
|
11
|
+
export declare function tokenCost(tokens: number | undefined, costPerMillion: number | undefined): number;
|
|
12
|
+
/**
|
|
13
|
+
* Strip surrounding markdown code fences from a string. Returns the raw
|
|
14
|
+
* input unchanged if no fences are present. Handles both the leading
|
|
15
|
+
* ` ```json ` (case-insensitive, with optional trailing whitespace) and a
|
|
16
|
+
* trailing ` ``` ` on its own line. Useful when LLMs return JSON wrapped
|
|
17
|
+
* in markdown despite being asked for raw JSON.
|
|
18
|
+
*/
|
|
19
|
+
export declare function stripCodeFence(s: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* Return a shallow copy of `obj` with all `undefined` values stripped out.
|
|
22
|
+
* Useful when building request payloads where optional fields should only
|
|
23
|
+
* appear when explicitly set.
|
|
24
|
+
*/
|
|
25
|
+
export declare function omitUndefined<T extends Record<string, unknown>>(obj: T): T;
|
|
3
26
|
/**
|
|
4
27
|
* Sanitizes an object by removing keys that could cause prototype pollution.
|
|
5
28
|
* Returns a shallow copy with dangerous keys filtered out.
|
package/dist/util/util.js
CHANGED
|
@@ -3,6 +3,46 @@ export function round(num, places) {
|
|
|
3
3
|
const factor = Math.pow(10, places);
|
|
4
4
|
return Math.round(num * factor) / factor;
|
|
5
5
|
}
|
|
6
|
+
/** Token-cost prices in the model registry are quoted per 1M tokens. */
|
|
7
|
+
export const TOKENS_PER_MILLION = 1_000_000;
|
|
8
|
+
/** Round all dollar costs to 6 decimal places (1/100 of a cent). */
|
|
9
|
+
export const COST_DECIMAL_PLACES = 6;
|
|
10
|
+
/**
|
|
11
|
+
* Compute the dollar cost of `tokens` at `costPerMillion` (USD per 1M tokens).
|
|
12
|
+
* Returns 0 if either argument is missing or non-positive.
|
|
13
|
+
*/
|
|
14
|
+
export function tokenCost(tokens, costPerMillion) {
|
|
15
|
+
if (!tokens || !costPerMillion || tokens <= 0)
|
|
16
|
+
return 0;
|
|
17
|
+
return round((tokens * costPerMillion) / TOKENS_PER_MILLION, COST_DECIMAL_PLACES);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Strip surrounding markdown code fences from a string. Returns the raw
|
|
21
|
+
* input unchanged if no fences are present. Handles both the leading
|
|
22
|
+
* ` ```json ` (case-insensitive, with optional trailing whitespace) and a
|
|
23
|
+
* trailing ` ``` ` on its own line. Useful when LLMs return JSON wrapped
|
|
24
|
+
* in markdown despite being asked for raw JSON.
|
|
25
|
+
*/
|
|
26
|
+
export function stripCodeFence(s) {
|
|
27
|
+
return s
|
|
28
|
+
.trim()
|
|
29
|
+
.replace(/^```(?:json)?\s*/i, "")
|
|
30
|
+
.replace(/\s*```\s*$/, "");
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Return a shallow copy of `obj` with all `undefined` values stripped out.
|
|
34
|
+
* Useful when building request payloads where optional fields should only
|
|
35
|
+
* appear when explicitly set.
|
|
36
|
+
*/
|
|
37
|
+
export function omitUndefined(obj) {
|
|
38
|
+
const result = {};
|
|
39
|
+
for (const key of Object.keys(obj)) {
|
|
40
|
+
const value = obj[key];
|
|
41
|
+
if (value !== undefined)
|
|
42
|
+
result[key] = value;
|
|
43
|
+
}
|
|
44
|
+
return result;
|
|
45
|
+
}
|
|
6
46
|
const DANGEROUS_KEYS = new Set([
|
|
7
47
|
"__proto__",
|
|
8
48
|
"constructor",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "smoltalk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "A common interface for LLM APIs",
|
|
5
5
|
"homepage": "https://github.com/egonSchiele/smoltalk",
|
|
6
6
|
"files": [
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
39
39
|
"test": "vitest --exclude=**/*.live.test.ts",
|
|
40
|
-
"test:live": "vitest run lib/clients/*.live.test.ts",
|
|
40
|
+
"test:live": "vitest run lib/clients/*.live.test.ts lib/embed/*.live.test.ts lib/image/*.live.test.ts",
|
|
41
41
|
"test:tsc": "tsc -p tests/tsconfig.json",
|
|
42
42
|
"build": "rm -rf dist && tsc",
|
|
43
43
|
"start": "cd dist && node index.js",
|