smoltalk 0.5.0 → 0.5.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.
@@ -56,7 +56,7 @@ export class BaseClient {
56
56
  const messageLimitResult = this.checkMessageLimit(promptConfig);
57
57
  if (messageLimitResult)
58
58
  return messageLimitResult;
59
- const hostedError = validateHostedTools(promptConfig.hostedTools, promptConfig.model, promptConfig.modelData);
59
+ const hostedError = validateHostedTools(promptConfig.hostedTools, promptConfig.model, promptConfig.provider, promptConfig.modelData);
60
60
  if (hostedError) {
61
61
  return failure(hostedError);
62
62
  }
@@ -269,7 +269,7 @@ export class BaseClient {
269
269
  };
270
270
  return;
271
271
  }
272
- const hostedError = validateHostedTools(config.hostedTools, config.model, config.modelData);
272
+ const hostedError = validateHostedTools(config.hostedTools, config.model, config.provider, config.modelData);
273
273
  if (hostedError) {
274
274
  yield { type: "error", error: hostedError };
275
275
  return;
package/dist/models.js CHANGED
@@ -1600,9 +1600,16 @@ export function getHostedTools(opts = {}) {
1600
1600
  if (opts.modelData) {
1601
1601
  tools = mergeHostedTools(tools, opts.modelData.hostedTools);
1602
1602
  }
1603
+ // A provider override is authoritative: it decides which API the model routes
1604
+ // through, and therefore which hosted tools are available for it.
1603
1605
  let modelProvider;
1604
1606
  if (opts.model) {
1605
- modelProvider = getModel(opts.model, opts.modelData)?.provider;
1607
+ if (opts.provider) {
1608
+ modelProvider = opts.provider;
1609
+ }
1610
+ else {
1611
+ modelProvider = getModel(opts.model, opts.modelData)?.provider;
1612
+ }
1606
1613
  }
1607
1614
  return tools.filter((tool) => {
1608
1615
  if (tool.disabled && !opts.includeDisabled) {
@@ -3,7 +3,7 @@ import type { CostEstimate } from "../types/costEstimate.js";
3
3
  import type { HostedToolResult, WebSearchSource, WebSearchCitation } from "../types.js";
4
4
  export declare const WEB_SEARCH = "web_search";
5
5
  export declare const IMPLEMENTED_HOSTED_TOOLS: Set<string>;
6
- export declare function validateHostedTools(requested: string[] | undefined, model: string, modelData?: ModelDataBlob): string | null;
6
+ export declare function validateHostedTools(requested: string[] | undefined, model: string, provider?: string, modelData?: ModelDataBlob): string | null;
7
7
  export declare function estimateHostedToolCost(result: HostedToolResult, model: string, modelData?: ModelDataBlob): number | undefined;
8
8
  export declare function foldHostedToolCost(cost: CostEstimate | undefined, results: HostedToolResult[]): CostEstimate | undefined;
9
9
  export declare function webSearchResult(provider: string, parts: {
@@ -6,12 +6,15 @@ export const WEB_SEARCH = "web_search";
6
6
  export const IMPLEMENTED_HOSTED_TOOLS = new Set([WEB_SEARCH]);
7
7
  // Returns an error message if any requested capability is unusable for this
8
8
  // model, else null. Capabilities are matched against the catalog `category`.
9
- export function validateHostedTools(requested, model, modelData) {
9
+ // A `provider` override is authoritative: the same model can route through a
10
+ // different API (e.g. gpt-4o-mini via "openai-responses"), which changes which
11
+ // hosted tools are available.
12
+ export function validateHostedTools(requested, model, provider, modelData) {
10
13
  if (!requested || requested.length === 0) {
11
14
  return null;
12
15
  }
13
16
  const allCategories = new Set(getHostedTools({ includeDisabled: true, modelData }).map((t) => t.category));
14
- const availableCategories = new Set(getHostedTools({ model, modelData }).map((t) => t.category));
17
+ const availableCategories = new Set(getHostedTools({ model, provider, modelData }).map((t) => t.category));
15
18
  for (const name of requested) {
16
19
  if (!IMPLEMENTED_HOSTED_TOOLS.has(name)) {
17
20
  if (allCategories.has(name)) {
@@ -20,8 +23,11 @@ export function validateHostedTools(requested, model, modelData) {
20
23
  return `Unknown hosted tool "${name}".`;
21
24
  }
22
25
  if (!availableCategories.has(name)) {
23
- const provider = getModel(model, modelData)?.provider ?? "unknown";
24
- return `${name} is a hosted capability; ${model} (${provider}) doesn't offer it — pass a search function as a tool instead.`;
26
+ let effectiveProvider = provider;
27
+ if (!effectiveProvider) {
28
+ effectiveProvider = getModel(model, modelData)?.provider ?? "unknown";
29
+ }
30
+ return `${name} is a hosted capability; ${model} (${effectiveProvider}) doesn't offer it — pass a search function as a tool instead.`;
25
31
  }
26
32
  }
27
33
  return null;
@@ -32,7 +38,15 @@ export function estimateHostedToolCost(result, model, modelData) {
32
38
  if (!result.callCount) {
33
39
  return undefined;
34
40
  }
35
- const tools = getHostedTools({ model, includeDisabled: true, modelData });
41
+ // The result names the provider that produced it, which may differ from the
42
+ // model's registered provider (e.g. a base model routed through Responses).
43
+ // Use it so the catalog lookup isn't filtered out by the wrong provider.
44
+ const tools = getHostedTools({
45
+ model,
46
+ provider: result.provider,
47
+ includeDisabled: true,
48
+ modelData,
49
+ });
36
50
  const tool = tools.find((t) => t.category === result.tool && t.provider === result.provider);
37
51
  if (!tool) {
38
52
  return undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smoltalk",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "A common interface for LLM APIs",
5
5
  "homepage": "https://github.com/egonSchiele/smoltalk",
6
6
  "files": [