smoltalk 0.0.67 → 0.2.1

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.
Files changed (54) hide show
  1. package/README.md +51 -154
  2. package/dist/client.d.ts +3 -3
  3. package/dist/client.js +9 -5
  4. package/dist/clients/anthropic.d.ts +4 -4
  5. package/dist/clients/anthropic.js +1 -1
  6. package/dist/clients/baseClient.d.ts +17 -20
  7. package/dist/clients/baseClient.js +21 -43
  8. package/dist/clients/google.d.ts +4 -4
  9. package/dist/clients/google.js +1 -1
  10. package/dist/clients/ollama.d.ts +4 -4
  11. package/dist/clients/ollama.js +1 -1
  12. package/dist/clients/openai.d.ts +4 -4
  13. package/dist/clients/openai.js +2 -1
  14. package/dist/clients/openaiResponses.d.ts +4 -4
  15. package/dist/clients/openaiResponses.js +2 -1
  16. package/dist/functions.d.ts +13 -10
  17. package/dist/functions.js +4 -55
  18. package/dist/index.d.ts +2 -4
  19. package/dist/index.js +1 -2
  20. package/dist/model.d.ts +2 -5
  21. package/dist/model.js +11 -27
  22. package/dist/models.d.ts +2 -2
  23. package/dist/models.js +3 -1
  24. package/dist/testing/index.d.ts +9 -0
  25. package/dist/testing/index.js +41 -0
  26. package/dist/types.d.ts +52 -160
  27. package/dist/types.js +1 -1
  28. package/dist/util/logger.d.ts +17 -1
  29. package/dist/util/logger.js +68 -5
  30. package/package.json +15 -19
  31. package/dist/clients/llamaCpp.d.ts +0 -28
  32. package/dist/clients/llamaCpp.js +0 -316
  33. package/dist/latencyTracker.d.ts +0 -32
  34. package/dist/latencyTracker.js +0 -73
  35. package/dist/middleware.d.ts +0 -54
  36. package/dist/middleware.js +0 -321
  37. package/dist/strategies/baseStrategy.d.ts +0 -22
  38. package/dist/strategies/baseStrategy.js +0 -62
  39. package/dist/strategies/fallbackStrategy.d.ts +0 -14
  40. package/dist/strategies/fallbackStrategy.js +0 -122
  41. package/dist/strategies/fastestStrategy.d.ts +0 -19
  42. package/dist/strategies/fastestStrategy.js +0 -108
  43. package/dist/strategies/idStrategy.d.ts +0 -16
  44. package/dist/strategies/idStrategy.js +0 -62
  45. package/dist/strategies/index.d.ts +0 -17
  46. package/dist/strategies/index.js +0 -68
  47. package/dist/strategies/raceStrategy.d.ts +0 -12
  48. package/dist/strategies/raceStrategy.js +0 -72
  49. package/dist/strategies/randomStrategy.d.ts +0 -13
  50. package/dist/strategies/randomStrategy.js +0 -54
  51. package/dist/strategies/timeoutStrategy.d.ts +0 -13
  52. package/dist/strategies/timeoutStrategy.js +0 -65
  53. package/dist/strategies/types.d.ts +0 -78
  54. package/dist/strategies/types.js +0 -58
@@ -1,8 +1,8 @@
1
1
  import OpenAI from "openai";
2
- import { BaseClientConfig, PromptConfig, PromptResult, Result, SmolClient, StreamChunk } from "../types.js";
2
+ import { PromptResult, Result, SmolClient, SmolConfig, StreamChunk } from "../types.js";
3
3
  import { BaseClient } from "./baseClient.js";
4
4
  import { ModelName } from "../models.js";
5
- export type SmolOpenAiConfig = BaseClientConfig;
5
+ export type SmolOpenAiConfig = SmolConfig;
6
6
  export declare class SmolOpenAi extends BaseClient implements SmolClient {
7
7
  private client;
8
8
  private logger;
@@ -13,6 +13,6 @@ export declare class SmolOpenAi extends BaseClient implements SmolClient {
13
13
  private calculateUsageAndCost;
14
14
  private buildRequest;
15
15
  private rethrowAsSmolError;
16
- _textSync(config: PromptConfig): Promise<Result<PromptResult>>;
17
- _textStream(config: PromptConfig): AsyncGenerator<StreamChunk>;
16
+ _textSync(config: SmolConfig): Promise<Result<PromptResult>>;
17
+ _textStream(config: SmolConfig): AsyncGenerator<StreamChunk>;
18
18
  }
@@ -24,7 +24,7 @@ export class SmolOpenAi extends BaseClient {
24
24
  return this.client;
25
25
  }
26
26
  getModel() {
27
- return this.model.getResolvedModel();
27
+ return this.model.getModel();
28
28
  }
29
29
  calculateUsageAndCost(usageData) {
30
30
  let usage;
@@ -64,6 +64,7 @@ export class SmolOpenAi extends BaseClient {
64
64
  json_schema: {
65
65
  name: config.responseFormatOptions?.name || "response",
66
66
  schema: config.responseFormat.toJSONSchema(),
67
+ strict: !!config.responseFormatOptions?.strict,
67
68
  },
68
69
  };
69
70
  }
@@ -1,8 +1,8 @@
1
1
  import OpenAI from "openai";
2
- import { BaseClientConfig, PromptConfig, PromptResult, Result, SmolClient, StreamChunk } from "../types.js";
2
+ import { PromptResult, Result, SmolClient, SmolConfig, StreamChunk } from "../types.js";
3
3
  import { BaseClient } from "./baseClient.js";
4
4
  import { ModelName } from "../models.js";
5
- export type SmolOpenAiResponsesConfig = BaseClientConfig;
5
+ export type SmolOpenAiResponsesConfig = SmolConfig;
6
6
  export declare class SmolOpenAiResponses extends BaseClient implements SmolClient {
7
7
  private client;
8
8
  private logger;
@@ -14,6 +14,6 @@ export declare class SmolOpenAiResponses extends BaseClient implements SmolClien
14
14
  private buildRequest;
15
15
  private calculateUsageAndCost;
16
16
  private rethrowAsSmolError;
17
- _textSync(config: PromptConfig): Promise<Result<PromptResult>>;
18
- _textStream(config: PromptConfig): AsyncGenerator<StreamChunk>;
17
+ _textSync(config: SmolConfig): Promise<Result<PromptResult>>;
18
+ _textStream(config: SmolConfig): AsyncGenerator<StreamChunk>;
19
19
  }
@@ -24,7 +24,7 @@ export class SmolOpenAiResponses extends BaseClient {
24
24
  return this.client;
25
25
  }
26
26
  getModel() {
27
- return this.model.getResolvedModel();
27
+ return this.model.getModel();
28
28
  }
29
29
  convertMessages(config) {
30
30
  const systemParts = [];
@@ -76,6 +76,7 @@ export class SmolOpenAiResponses extends BaseClient {
76
76
  type: "json_schema",
77
77
  name: config.responseFormatOptions?.name || "response",
78
78
  schema: config.responseFormat.toJSONSchema(),
79
+ strict: !!config.responseFormatOptions?.strict,
79
80
  },
80
81
  };
81
82
  }
@@ -1,15 +1,18 @@
1
- import { getClient } from "./client.js";
2
- import { PromptConfig, PromptResult, SmolPromptConfig, StreamChunk } from "./types.js";
1
+ import { PromptResult, SmolConfig, StreamChunk } from "./types.js";
3
2
  import { Result } from "./types/result.js";
4
- export declare function splitConfig(config: SmolPromptConfig): {
5
- smolConfig: Parameters<typeof getClient>[0];
6
- promptConfig: PromptConfig;
7
- };
8
- export declare function text(config: SmolPromptConfig & {
3
+ import type { z, ZodType } from "zod";
4
+ export declare function text(config: SmolConfig & {
9
5
  stream: true;
10
6
  }): AsyncGenerator<StreamChunk>;
11
- export declare function text(config: SmolPromptConfig & {
7
+ export declare function text<S extends ZodType>(config: Omit<SmolConfig, "responseFormat"> & {
8
+ responseFormat: S;
9
+ stream?: false;
10
+ }): Promise<Result<PromptResult<z.infer<S>>>>;
11
+ export declare function text(config: SmolConfig & {
12
12
  stream?: false;
13
13
  }): Promise<Result<PromptResult>>;
14
- export declare function textSync(config: SmolPromptConfig): Promise<Result<PromptResult>>;
15
- export declare function textStream(config: SmolPromptConfig): AsyncGenerator<StreamChunk>;
14
+ export declare function textSync<S extends ZodType>(config: Omit<SmolConfig, "responseFormat"> & {
15
+ responseFormat: S;
16
+ }): Promise<Result<PromptResult<z.infer<S>>>>;
17
+ export declare function textSync(config: SmolConfig): Promise<Result<PromptResult>>;
18
+ export declare function textStream(config: SmolConfig): AsyncGenerator<StreamChunk>;
package/dist/functions.js CHANGED
@@ -1,42 +1,6 @@
1
1
  import { BaseMessage, messageFromJSON, } from "./classes/message/index.js";
2
- import { executeMiddlewareSync, executeMiddlewareStream } from "./middleware.js";
3
- import { Model } from "./model.js";
4
- import { BaseStrategy } from "./strategies/baseStrategy.js";
5
- import { fromJSON } from "./strategies/index.js";
2
+ import { getClient } from "./client.js";
6
3
  import { getLogger } from "./util/logger.js";
7
- function getStrategy(model) {
8
- if (model instanceof BaseStrategy)
9
- return model;
10
- return fromJSON(model);
11
- }
12
- /** Always creates a fresh strategy instance (safe for concurrent use). */
13
- function getFreshStrategy(model) {
14
- if (model instanceof BaseStrategy)
15
- return fromJSON(model.toJSON());
16
- return fromJSON(model);
17
- }
18
- export function splitConfig(config) {
19
- const { openAiApiKey, googleApiKey, ollamaApiKey, anthropicApiKey, ollamaHost, model: rawModel, provider, logLevel, statelog, metadata, hooks, llamaCppModelDir, middleware, ...promptConfig } = config;
20
- const _model = new Model(rawModel);
21
- const model = _model.getResolvedModel();
22
- return {
23
- smolConfig: {
24
- openAiApiKey,
25
- googleApiKey,
26
- ollamaApiKey,
27
- anthropicApiKey,
28
- ollamaHost,
29
- model,
30
- provider,
31
- logLevel,
32
- statelog,
33
- metadata,
34
- hooks,
35
- llamaCppModelDir,
36
- },
37
- promptConfig,
38
- };
39
- }
40
4
  function fixMessagesIfNecessary(messages) {
41
5
  if (messages && messages.length > 0) {
42
6
  if (!(messages[0] instanceof BaseMessage)) {
@@ -47,30 +11,15 @@ function fixMessagesIfNecessary(messages) {
47
11
  return messages;
48
12
  }
49
13
  export function text(config) {
50
- if (config.stream) {
14
+ if (config.stream)
51
15
  return textStream(config);
52
- }
53
16
  return textSync(config);
54
17
  }
55
18
  export async function textSync(config) {
56
19
  config.messages = fixMessagesIfNecessary(config.messages);
57
- if (config.middleware && config.middleware.checks.length > 0) {
58
- const runMain = (cfg) => { const s = getFreshStrategy(cfg.model); return s.textSync(cfg); };
59
- const middlewareResult = await executeMiddlewareSync(config, runMain, runMain);
60
- if (middlewareResult)
61
- return middlewareResult;
62
- }
63
- const strategy = getStrategy(config.model);
64
- const { middleware: _, ...configWithoutMiddleware } = config;
65
- return strategy.textSync(configWithoutMiddleware);
20
+ return getClient(config).textSync(config);
66
21
  }
67
22
  export async function* textStream(config) {
68
23
  config.messages = fixMessagesIfNecessary(config.messages);
69
- if (config.middleware && config.middleware.checks.length > 0) {
70
- yield* executeMiddlewareStream(config, (cfg) => { const s = getFreshStrategy(cfg.model); return s.textStream(cfg); }, (cfg) => { const s = getFreshStrategy(cfg.model); return s.textSync(cfg); });
71
- return;
72
- }
73
- const strategy = getStrategy(config.model);
74
- const { middleware: _, ...configWithoutMiddleware } = config;
75
- yield* strategy.textStream(configWithoutMiddleware);
24
+ yield* getClient(config).textStream(config);
76
25
  }
package/dist/index.d.ts CHANGED
@@ -7,7 +7,5 @@ export * from "./util/util.js";
7
7
  export * from "./classes/message/index.js";
8
8
  export * from "./functions.js";
9
9
  export * from "./classes/ToolCall.js";
10
- export * from "./strategies/index.js";
11
- export { latencyTracker } from "./latencyTracker.js";
12
- export type { LatencySample } from "./latencyTracker.js";
13
- export type { MiddlewareCheck, MiddlewareConfig, MiddlewareResult } from "./middleware.js";
10
+ export { getLogger, EgonLog } from "./util/logger.js";
11
+ export type { LogLevel } from "./util/logger.js";
package/dist/index.js CHANGED
@@ -7,5 +7,4 @@ export * from "./util/util.js";
7
7
  export * from "./classes/message/index.js";
8
8
  export * from "./functions.js";
9
9
  export * from "./classes/ToolCall.js";
10
- export * from "./strategies/index.js";
11
- export { latencyTracker } from "./latencyTracker.js";
10
+ export { getLogger, EgonLog } from "./util/logger.js";
package/dist/model.d.ts CHANGED
@@ -2,14 +2,11 @@ import { ModelName, Provider } from "./models.js";
2
2
  import { ModelLike } from "./types.js";
3
3
  export declare class Model {
4
4
  private model;
5
- private resolvedModel;
6
5
  private provider?;
7
6
  constructor(model: ModelName, provider?: Provider);
8
- getModel(): string;
9
- getResolvedModel(): string;
7
+ getModel(): ModelName;
10
8
  getProvider(): Provider | undefined;
11
- setProvider(): Provider | undefined;
12
- resolveModel(): ModelName;
9
+ private lookupProvider;
13
10
  calculateCost(usage: {
14
11
  inputTokens: number;
15
12
  outputTokens: number;
package/dist/model.js CHANGED
@@ -1,44 +1,28 @@
1
- import { getModel, isTextModel } from "./models.js";
1
+ import { getModel, isTextModel, ModelNameSchema } from "./models.js";
2
2
  import { SmolError } from "./smolError.js";
3
- import { ModelNameSchema } from "./strategies/types.js";
4
3
  import { round } from "./util/util.js";
5
4
  export class Model {
6
5
  model;
7
- resolvedModel;
8
6
  provider;
9
7
  constructor(model, provider) {
8
+ if (!ModelNameSchema.safeParse(model).success) {
9
+ throw new SmolError(`Model ${JSON.stringify(model)} is not recognized. Please specify a known model name.`);
10
+ }
10
11
  this.model = model;
11
- this.resolvedModel = this.resolveModel();
12
- this.provider = provider || this.setProvider();
12
+ this.provider = provider || this.lookupProvider();
13
13
  }
14
14
  getModel() {
15
15
  return this.model;
16
16
  }
17
- getResolvedModel() {
18
- return this.resolvedModel;
19
- }
20
17
  getProvider() {
21
- if (this.provider) {
22
- return this.provider;
23
- }
24
- return undefined;
18
+ return this.provider;
25
19
  }
26
- setProvider() {
27
- const resolved = this.getResolvedModel();
28
- const modelInfo = getModel(resolved);
29
- if (modelInfo) {
30
- return modelInfo.provider;
31
- }
32
- return undefined;
33
- }
34
- resolveModel() {
35
- if (ModelNameSchema.safeParse(this.model).success) {
36
- return this.model;
37
- }
38
- throw new SmolError(`Model ${JSON.stringify(this.model)} is not recognized. Please specify a known model name.`);
20
+ lookupProvider() {
21
+ const modelInfo = getModel(this.model);
22
+ return modelInfo ? modelInfo.provider : undefined;
39
23
  }
40
24
  calculateCost(usage) {
41
- const model = getModel(this.getResolvedModel());
25
+ const model = getModel(this.model);
42
26
  if (!model || !isTextModel(model)) {
43
27
  return null;
44
28
  }
@@ -60,7 +44,7 @@ export class Model {
60
44
  return `Model(${JSON.stringify(this.model)})`;
61
45
  }
62
46
  toJSON() {
63
- return this.getResolvedModel();
47
+ return this.model;
64
48
  }
65
49
  static create(model, provider) {
66
50
  if (model instanceof Model) {
package/dist/models.d.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  import { z } from "zod";
2
- export declare const providers: readonly ["ollama", "llama-cpp", "openai", "openai-responses", "anthropic", "google", "replicate", "modal"];
2
+ export declare const providers: readonly ["ollama", "openai", "openai-responses", "anthropic", "google", "replicate", "modal"];
3
3
  export declare const ProviderSchema: z.ZodEnum<{
4
4
  ollama: "ollama";
5
- "llama-cpp": "llama-cpp";
6
5
  openai: "openai";
7
6
  "openai-responses": "openai-responses";
8
7
  anthropic: "anthropic";
@@ -1226,3 +1225,4 @@ export declare function isImageModel(model: ModelType): model is ImageModel;
1226
1225
  export declare function isTextModel(model: ModelType): model is TextModel;
1227
1226
  export declare function isSpeechToTextModel(model: ModelType): model is SpeechToTextModel;
1228
1227
  export declare function isEmbeddingsModel(model: ModelType): model is EmbeddingsModel;
1228
+ export declare const ModelNameSchema: z.ZodString;
package/dist/models.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import { z } from "zod";
2
2
  export const providers = [
3
3
  "ollama",
4
- "llama-cpp",
5
4
  "openai",
6
5
  "openai-responses",
7
6
  "anthropic",
@@ -673,3 +672,6 @@ export function isSpeechToTextModel(model) {
673
672
  export function isEmbeddingsModel(model) {
674
673
  return model.type === "embeddings";
675
674
  }
675
+ export const ModelNameSchema = z
676
+ .string()
677
+ .regex(/^[a-zA-Z0-9._:-]+$/, "Model name must only contain letters, numbers, dots, underscores, hyphens, and colons");
@@ -0,0 +1,9 @@
1
+ import { BaseClient } from "../clients/baseClient.js";
2
+ import type { PromptResult, Result, SmolConfig, StreamChunk } from "../types.js";
3
+ export declare class TestProvider extends BaseClient {
4
+ private callIndex;
5
+ private nextResponse;
6
+ private cannedUsage;
7
+ _textSync(config: SmolConfig): Promise<Result<PromptResult>>;
8
+ _textStream(config: SmolConfig): AsyncGenerator<StreamChunk>;
9
+ }
@@ -0,0 +1,41 @@
1
+ import { BaseClient } from "../clients/baseClient.js";
2
+ import { promptResult, success } from "../types.js";
3
+ const DEFAULT_RESPONSE = "test response";
4
+ export class TestProvider extends BaseClient {
5
+ callIndex = 0;
6
+ nextResponse(config) {
7
+ const responses = config.metadata?.testResponses;
8
+ if (Array.isArray(responses) && responses.length > 0) {
9
+ const response = responses[this.callIndex % responses.length];
10
+ this.callIndex += 1;
11
+ return response;
12
+ }
13
+ const single = config.metadata?.testResponse;
14
+ return single ?? DEFAULT_RESPONSE;
15
+ }
16
+ cannedUsage(config) {
17
+ return config.metadata?.testUsage;
18
+ }
19
+ async _textSync(config) {
20
+ const output = this.nextResponse(config);
21
+ return success(promptResult({
22
+ output,
23
+ toolCalls: [],
24
+ model: config.model,
25
+ usage: this.cannedUsage(config),
26
+ }));
27
+ }
28
+ async *_textStream(config) {
29
+ const output = this.nextResponse(config);
30
+ yield { type: "text", text: output };
31
+ yield {
32
+ type: "done",
33
+ result: promptResult({
34
+ output,
35
+ toolCalls: [],
36
+ model: config.model,
37
+ usage: this.cannedUsage(config),
38
+ }),
39
+ };
40
+ }
41
+ }
package/dist/types.d.ts CHANGED
@@ -1,21 +1,52 @@
1
1
  export * from "./types/result.js";
2
- import { LogLevel } from "egonlog";
3
- import type { MiddlewareConfig } from "./middleware.js";
2
+ import { LogLevel } from "./util/logger.js";
4
3
  import z, { ZodType } from "zod";
5
4
  import { Message } from "./classes/message/index.js";
6
5
  import { ToolCall } from "./classes/ToolCall.js";
7
6
  import { Model } from "./model.js";
8
7
  import { ModelName } from "./models.js";
9
- import { Strategy, StrategyJSON } from "./strategies/types.js";
10
8
  import { Result } from "./types/result.js";
11
9
  import { TokenUsage } from "./types/tokenUsage.js";
12
10
  import { CostEstimate } from "./types/costEstimate.js";
13
11
  export * from "./types/costEstimate.js";
14
12
  export * from "./types/tokenUsage.js";
15
- export type PromptConfig = {
13
+ export type SmolConfig = {
14
+ /** The model to use. */
15
+ model: ModelName;
16
+ /** Override the provider for the given model (e.g., use a custom endpoint for an OpenAI-compatible model). */
17
+ provider?: string;
18
+ /** API key for OpenAI. Required when using OpenAI models. */
19
+ openAiApiKey?: string;
20
+ /** API key for Google Gemini. Required when using Google models. */
21
+ googleApiKey?: string;
22
+ /** API key for Anthropic. Required when using Anthropic/Claude models. */
23
+ anthropicApiKey?: string;
24
+ /** API key for Ollama. Only needed when connecting to a cloud-hosted Ollama instance. */
25
+ ollamaApiKey?: string;
26
+ /** Base URL for the Ollama server. Defaults to localhost if not set. (Ollama only) */
27
+ ollamaHost?: string;
28
+ /** Log level for internal debug logging. */
29
+ logLevel?: LogLevel;
30
+ /** Configuration for Statelog observability/tracing integration. */
31
+ statelog?: Partial<{
32
+ host: string;
33
+ projectId: string;
34
+ traceId: string;
35
+ debugMode: boolean;
36
+ apiKey: string;
37
+ }>;
38
+ /** Lifecycle hooks called at various points during execution. */
39
+ hooks?: Partial<{
40
+ onStart: (config: SmolConfig) => void;
41
+ onToolCall: (toolCall: ToolCall) => void;
42
+ onEnd: (result: PromptResult) => void;
43
+ onError: (error: Error) => void;
44
+ }>;
45
+ /** Arbitrary metadata passed to custom model providers. */
46
+ metadata?: Record<string, any>;
16
47
  /** The conversation messages to send to the model. */
17
48
  messages: Message[];
18
- /** Tools (functions) the model can call. Each tool has a name, optional description, and a Zod schema defining its parameters. */
49
+ /** Tools (functions) the model can call. */
19
50
  tools?: {
20
51
  name: string;
21
52
  description?: string;
@@ -23,9 +54,9 @@ export type PromptConfig = {
23
54
  }[];
24
55
  /** Maximum number of tokens the model can generate in its response. */
25
56
  maxTokens?: number;
26
- /** Sampling temperature (0-2). Higher values make output more random, lower values more deterministic. (OpenAI only) */
57
+ /** Sampling temperature (0-2). (OpenAI only) */
27
58
  temperature?: number;
28
- /** Number of alternative completions to generate. Not currently used by any provider. */
59
+ /** Number of alternative completions to generate. */
29
60
  numSuggestions?: number;
30
61
  /** Whether the model can call multiple tools in a single turn. (OpenAI Responses API only) */
31
62
  parallelToolCalls?: boolean;
@@ -33,40 +64,17 @@ export type PromptConfig = {
33
64
  responseFormat?: ZodType;
34
65
  /** If true, returns an AsyncGenerator of StreamChunks instead of a single result. */
35
66
  stream?: boolean;
36
- /**
37
- * Enable extended thinking / thought signatures.
38
- * When enabled, the model returns its reasoning process alongside the response.
39
- * (Anthropic and Google only — OpenAI reasoning tokens are not exposed)
40
- */
67
+ /** Enable extended thinking / thought signatures. (Anthropic and Google only) */
41
68
  thinking?: {
42
- /** Whether to enable extended thinking. */
43
69
  enabled: boolean;
44
- /** Token budget for the thinking process. Defaults to 5000. (Anthropic only) */
45
70
  budgetTokens?: number;
46
71
  };
47
- /**
48
- * Provider-agnostic reasoning effort level.
49
- * - OpenAI: passed as reasoning_effort / reasoning.effort
50
- * - Anthropic: mapped to thinking budget (low=2048, medium=5000, high=10000)
51
- * - Google: mapped to thinkingBudget (low=2048, medium=8192, high=16384)
52
- * If `thinking` is also set, it takes precedence for Anthropic/Google.
53
- */
72
+ /** Provider-agnostic reasoning effort level. */
54
73
  reasoningEffort?: "low" | "medium" | "high";
55
74
  responseFormatOptions?: Partial<{
56
- /**
57
- * Identifier for the JSON schema sent to OpenAI's structured output API
58
- * (e.g. "math_response"). Defaults to "response".
59
- * OpenAI's json_schema response format *requires* a name field. The name helps
60
- * OpenAI identify and cache the schema. Smoltalk defaults it to "response"
61
- * so users don't have to think about it, but the option is there if someone
62
- * wants to set a more descriptive name (OpenAI recommends it for clarity in their docs).
63
- */
64
75
  name: string;
65
- /** Whether to enforce strict schema adherence. */
66
76
  strict: boolean;
67
- /** Number of retries if validation fails. Defaults to 2 when strict is true. */
68
77
  numRetries: number;
69
- /** If true, strip extra keys from the response instead of failing validation. */
70
78
  allowExtraKeys: boolean;
71
79
  }>;
72
80
  /** Arbitrary provider-specific attributes passed directly to the underlying API call. */
@@ -78,139 +86,21 @@ export type PromptConfig = {
78
86
  /** Define behavior if too many repeated tool calls are detected (loop prevention). */
79
87
  toolLoopDetection?: ToolLoopDetection;
80
88
  };
81
- export type SmolConfig = {
82
- /** API key for OpenAI. Required when using OpenAI models. */
83
- openAiApiKey?: string;
84
- /** API key for Google Gemini. Required when using Google models. */
85
- googleApiKey?: string;
86
- /** API key for Anthropic. Required when using Anthropic/Claude models. */
87
- anthropicApiKey?: string;
88
- /** API key for Ollama. Only needed when connecting to a cloud-hosted Ollama instance. */
89
- ollamaApiKey?: string;
90
- /** Base URL for the Ollama server. Defaults to localhost if not set. (Ollama only) */
91
- ollamaHost?: string;
92
- /** Directory path for Llama.cpp models. Required when using the Llama.cpp client. */
93
- llamaCppModelDir?: string;
94
- /**
95
- The given model determines both
96
- - what client is used
97
- - what strategy is executed.
98
-
99
- ## 1. Specifying a model directly
100
- The simplest case is to specify the name of a model from lib/models.ts.
101
- Example:
102
-
103
- ```
104
- model: "claude-sonnet-4-6"
105
- ```
106
-
107
- ## 2. Specifying a strategy
108
- You can instead specify a strategy to execute. For example:
109
-
110
- ```
111
- model: {
112
- type: "race",
113
- params: {
114
- strategies: ["gemini-2.5-flash-lite", "gemini-2.5-pro"],
115
- },
116
- }
117
- ```
118
-
119
- In this case, Smoltalk will run your request over using both LLMs simultaneously,
120
- and take the response that finishes first.
121
-
122
- You can also choose to specify fallbacks in case the first model
123
- returns an error for some reason. This can be a good way to try something
124
- with a fast model and then use a slower but more powerful model if the first one fails.
125
-
126
- ```
127
- model: {
128
- type: "fallback",
129
- params: {
130
- primaryStrategy: "gemini-2.5-flash-lite",
131
- config: {
132
- error: ["gemini-2.5-pro"],
133
- },
134
- },
135
- }
136
- ```
137
-
138
- You can of course combine strategies together to create more complex behavior:
139
-
140
- ```
141
- const geminiLiteWithFallback = {
142
- type: "fallback",
143
- params: {
144
- primaryStrategy: "gemini-2.5-flash-lite",
145
- config: {
146
- error: ["gemini-2.5-pro"],
147
- },
148
- },
149
- };
150
-
151
- model: {
152
- type: "race",
153
- params: {
154
- strategies: ["gemini-2.5-pro", geminiLiteWithFallback],
155
- },
156
- }
157
- ```
158
- */
159
- model: ModelParam;
160
- /** Override the provider for the given model (e.g., use a custom endpoint for an OpenAI-compatible model). */
161
- provider?: string;
162
- /** Log level for internal debug logging. */
163
- logLevel?: LogLevel;
164
- /** Configuration for Statelog observability/tracing integration. */
165
- statelog?: Partial<{
166
- /** Statelog server host URL. */
167
- host: string;
168
- /** Project identifier for grouping traces. */
169
- projectId: string;
170
- /** Trace identifier for correlating related requests. */
171
- traceId: string;
172
- /** Enable debug mode for verbose Statelog output. */
173
- debugMode: boolean;
174
- /** API key for authenticating with the Statelog server. */
175
- apiKey: string;
176
- }>;
177
- /** Lifecycle hooks called at various points during execution. */
178
- hooks?: Partial<{
179
- /** Called when the prompt execution starts. */
180
- onStart: (config: PromptConfig) => void;
181
- /** Called each time the model invokes a tool. */
182
- onToolCall: (toolCall: ToolCall) => void;
183
- /** Called when the prompt execution completes successfully. */
184
- onEnd: (result: PromptResult) => void;
185
- /** Called when an error occurs during execution. */
186
- onError: (error: Error) => void;
187
- /** Called when a strategy begins execution. */
188
- onStrategyStart: (strategy: Strategy, config: SmolPromptConfig) => void;
189
- }>;
190
- /** Arbitrary metadata passed to custom model providers. */
191
- metadata?: Record<string, any>;
192
- /** Middleware checks that run LLM-based validation on the prompt before or alongside the main call. */
193
- middleware?: MiddlewareConfig;
194
- };
195
89
  export type ToolLoopDetection = {
196
90
  enabled: boolean;
197
91
  maxCalls: number;
198
92
  intervention?: "remove-tool" | "remove-all-tools" | "throw-error" | "halt-execution";
199
93
  excludeTools?: string[];
200
94
  };
201
- export type ResolvedSmolConfig = Omit<SmolConfig, "model"> & {
202
- model: ModelName;
203
- };
204
- export type BaseClientConfig = ResolvedSmolConfig;
205
- export type PromptResult = {
206
- output: string | null;
95
+ export type PromptResult<T = string> = {
96
+ output: T | null;
207
97
  toolCalls: ToolCall[];
208
98
  thinkingBlocks?: ThinkingBlock[];
209
99
  usage?: TokenUsage;
210
100
  cost?: CostEstimate;
211
101
  model?: ModelName;
212
102
  };
213
- export declare function promptResult({ output, toolCalls, thinkingBlocks, usage, cost, model, }: Partial<PromptResult>): PromptResult;
103
+ export declare function promptResult<T = string>({ output, toolCalls, thinkingBlocks, usage, cost, model, }: Partial<PromptResult<T>>): PromptResult<T>;
214
104
  export type StreamChunk = {
215
105
  type: "text";
216
106
  text: string;
@@ -232,19 +122,21 @@ export type StreamChunk = {
232
122
  error: string;
233
123
  };
234
124
  export interface SmolClient {
235
- text(promptConfig: PromptConfig): Promise<Result<PromptResult>> | AsyncGenerator<StreamChunk>;
236
- textSync(config: PromptConfig): Promise<Result<PromptResult>>;
237
- _textSync(config: PromptConfig): Promise<Result<PromptResult>>;
238
- textStream(config: PromptConfig): AsyncGenerator<StreamChunk>;
239
- _textStream(config: PromptConfig): AsyncGenerator<StreamChunk>;
125
+ text(config: SmolConfig): Promise<Result<PromptResult>> | AsyncGenerator<StreamChunk>;
126
+ textSync(config: SmolConfig): Promise<Result<PromptResult>>;
127
+ _textSync(config: SmolConfig): Promise<Result<PromptResult>>;
128
+ textStream(config: SmolConfig): AsyncGenerator<StreamChunk>;
129
+ _textStream(config: SmolConfig): AsyncGenerator<StreamChunk>;
240
130
  }
241
- export type SmolPromptConfig = PromptConfig & SmolConfig;
242
131
  export type TextPart = {
243
132
  type: "text";
244
133
  text: string;
245
134
  };
135
+ /** Loose variant of SmolConfig for `getClient()` — messages are not required at construction time. */
136
+ export type SmolClientConfig = Omit<SmolConfig, "messages"> & {
137
+ messages?: Message[];
138
+ };
246
139
  export type ModelLike = ModelName | Model;
247
- export type ModelParam = ModelName | Strategy | StrategyJSON;
248
140
  export type ThinkingBlock = {
249
141
  text: string;
250
142
  signature: string;
package/dist/types.js CHANGED
@@ -4,7 +4,7 @@ export * from "./types/costEstimate.js";
4
4
  export * from "./types/tokenUsage.js";
5
5
  export function promptResult({ output, toolCalls, thinkingBlocks, usage, cost, model, }) {
6
6
  return {
7
- output: output || null,
7
+ output: (output ?? null),
8
8
  toolCalls: toolCalls || [],
9
9
  thinkingBlocks: thinkingBlocks,
10
10
  usage,