@combycode/llm-sdk 1.1.0 → 1.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/CHANGELOG.md +50 -0
- package/README.md +24 -0
- package/dist/agent/guardrail-types.d.ts +25 -0
- package/dist/agent/loop-config.d.ts +5 -1
- package/dist/agent/loop.d.ts +11 -1
- package/dist/agent/types.d.ts +8 -0
- package/dist/helpers/one-shot.d.ts +8 -1
- package/dist/index.browser.js +341 -23
- package/dist/index.d.ts +2 -1
- package/dist/index.js +341 -23
- package/dist/llm/client.d.ts +12 -1
- package/dist/llm/files/retrieve.d.ts +44 -0
- package/dist/llm/providers/anthropic/messages.d.ts +3 -0
- package/dist/llm/providers/google/generate.d.ts +7 -0
- package/dist/llm/providers/google/interactions.d.ts +2 -0
- package/dist/llm/providers/openai/completions.d.ts +2 -0
- package/dist/llm/providers/openai/responses.d.ts +3 -0
- package/dist/llm/types/provider.d.ts +9 -1
- package/dist/llm/types/response.d.ts +7 -0
- package/dist/llm/types/stream.d.ts +9 -1
- package/dist/network/types.d.ts +4 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,55 @@ All notable changes to `@combycode/llm-sdk` are documented here. The format foll
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.3.0] - 2026-07-06
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- **Streaming file parity.** `stream()` now surfaces hosted code-execution output files with the
|
|
13
|
+
same coverage as `complete()`: a new `{ type: 'file', file }` `StreamEvent` is emitted as each
|
|
14
|
+
file finalizes mid-stream, and the files are collected onto the streamed final response's
|
|
15
|
+
`files` (via `onCompletion` / the agent final response). New `ProviderAdapter.createStreamParser()`
|
|
16
|
+
returns a per-stream parser so adapters can hold per-stream state — used by Google to route an
|
|
17
|
+
inline code-execution artifact (whose "code ran" marker and bytes arrive in separate SSE events)
|
|
18
|
+
to `files` rather than conversational `media`. Verified live on Anthropic, OpenAI, and Google.
|
|
19
|
+
- **File-content retrieval** for hosted-tool output files. `CompleteResult`, `LLMClient`, and
|
|
20
|
+
the agent result now expose `retrieveFile(file)` → `{ blob, name, mimeType, size }` and
|
|
21
|
+
`streamFile(file)` → `{ stream, name, mimeType, size }`, resolving a `FileOutput`'s inline
|
|
22
|
+
`data`, `url`, or provider file `id` through the same model + key the call used — no
|
|
23
|
+
re-passing credentials. `streamFile` pipes large files to a sink without buffering. Name /
|
|
24
|
+
mime / size come from the download response headers (Content-Disposition / Content-Type /
|
|
25
|
+
Content-Length, with a filename-extension mime fallback). New network `responseType: 'stream'`
|
|
26
|
+
returns the raw body and releases the queue slot immediately. Exports `RetrievedFile`,
|
|
27
|
+
`FileStream`. Auth is sent only to the provider's own host.
|
|
28
|
+
|
|
29
|
+
## [1.2.0] - 2026-07-05
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
- Anthropic hosted code-execution **file outputs** now surface on `response.files` (verified
|
|
33
|
+
live). Three fixes, found by real-key testing: (1) the producer parsed the outdated
|
|
34
|
+
`code_execution_tool_result` shape, but the current `code_execution_20260521` tool emits
|
|
35
|
+
`bash_code_execution_tool_result` → `bash_code_execution_output.file_id` (now both are
|
|
36
|
+
handled); (2) code execution needs the beta endpoint — requests using `code_interpreter` now
|
|
37
|
+
hit `/v1/messages?beta=true`, without which no file outputs are returned; (3) `AgentLoop`'s
|
|
38
|
+
final response dropped `files` (and `moderation`) — both are now propagated from the final
|
|
39
|
+
LLM response in `complete()` and `stream()`.
|
|
40
|
+
|
|
41
|
+
### Added
|
|
42
|
+
- `AgentLoopConfig.toolInputGuardrails` (`ToolInputGuardrail[]`) — per-tool-call input
|
|
43
|
+
guardrails that validate a call's arguments before the permission/approval check. A trip
|
|
44
|
+
denies just that call (error result to the model) without halting the run or invoking the
|
|
45
|
+
HITL approver; a pass runs the normal permission/approval/execution path.
|
|
46
|
+
- `AgentTool.customDataExtractor` — optional hook to derive out-of-band metadata from a
|
|
47
|
+
successful tool result, attached to that call's `ToolCallReport.customData`. The model never
|
|
48
|
+
sees it (for your own telemetry/routing/audit); a throwing extractor is swallowed.
|
|
49
|
+
- Code-execution **file outputs** now surface on `response.files` across **all** providers
|
|
50
|
+
(completes the channel shipped in 1.1, which had only the Anthropic producer):
|
|
51
|
+
- OpenAI Responses: code-interpreter image outputs (by URL) and downloadable container
|
|
52
|
+
files (`container_file_citation` → file id + name).
|
|
53
|
+
- Google: hosted code-execution `inlineData` artifacts (base64), routed to `files`
|
|
54
|
+
instead of `media` when the turn used code execution.
|
|
55
|
+
- xAI: inherited from the OpenAI Responses adapter.
|
|
56
|
+
- `FileOutput` gains a `url` field (for providers that return a fetchable URL).
|
|
57
|
+
|
|
9
58
|
## [1.1.0] - 2026-06-30
|
|
10
59
|
|
|
11
60
|
### Added
|
|
@@ -53,5 +102,6 @@ First public release.
|
|
|
53
102
|
- Service tiers end to end (request → bill → cost).
|
|
54
103
|
- Cross-environment: runs on Node, Bun, and the browser. ESM, zero runtime deps.
|
|
55
104
|
|
|
105
|
+
[1.2.0]: https://github.com/combycode/llm-sdk-ts/releases/tag/v1.2.0
|
|
56
106
|
[1.1.0]: https://github.com/combycode/llm-sdk-ts/releases/tag/v1.1.0
|
|
57
107
|
[1.0.0]: https://github.com/combycode/llm-sdk-ts/releases/tag/v1.0.0
|
package/README.md
CHANGED
|
@@ -64,6 +64,28 @@ for await (const ev of llm.stream('Count to 5.')) {
|
|
|
64
64
|
}
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
+
### Hosted code execution + output files
|
|
68
|
+
|
|
69
|
+
Run the provider's code interpreter and pull the files it produces (charts, CSVs) — one
|
|
70
|
+
interface, any provider. `retrieveFile` / `streamFile` fetch the bytes plus `name` / `mimeType`
|
|
71
|
+
/ `size`, bound to the same model + key:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
import { complete } from '@combycode/llm-sdk';
|
|
75
|
+
|
|
76
|
+
const { response, retrieveFile } = await complete({
|
|
77
|
+
model: 'anthropic/claude-haiku-4.5',
|
|
78
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
79
|
+
prompt: 'Plot y = x**2 for x in 1..5, save a PNG, and return the file.',
|
|
80
|
+
tools: [{ type: 'code_interpreter' }],
|
|
81
|
+
maxTokens: 6000,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
for (const file of response.files ?? []) {
|
|
85
|
+
const { blob, name, mimeType, size } = await retrieveFile(file); // or streamFile for large files
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
67
89
|
## Documentation
|
|
68
90
|
|
|
69
91
|
Full guide pages covering all export groups:
|
|
@@ -77,6 +99,8 @@ Full guide pages covering all export groups:
|
|
|
77
99
|
- [Cost Tracking + estimate()](./docs/guide/cost.md)
|
|
78
100
|
- [Observability / Telemetry](./docs/guide/telemetry.md)
|
|
79
101
|
- [Media / Files / Batch](./docs/guide/media-files-batch.md)
|
|
102
|
+
- [Hosted Code Execution](./docs/guide/code-execution.md)
|
|
103
|
+
- [Retrieving Output Files](./docs/guide/retrieving-files.md)
|
|
80
104
|
- [MCP (Model Context Protocol)](./docs/guide/mcp.md)
|
|
81
105
|
- [Context Guard + Permissions + Persistence + Cache](./docs/guide/context-guard.md)
|
|
82
106
|
- [OpenAI-Compatible Server](./docs/guide/server.md)
|
|
@@ -46,6 +46,31 @@ export interface Guardrail {
|
|
|
46
46
|
/** Return a decision; throw only for unexpected infrastructure errors. */
|
|
47
47
|
check(ctx: GuardrailCheckContext): Promise<GuardrailDecision>;
|
|
48
48
|
}
|
|
49
|
+
/** Context for a tool-input guardrail: the specific tool call about to run. */
|
|
50
|
+
export interface ToolInputGuardrailContext {
|
|
51
|
+
toolName: string;
|
|
52
|
+
arguments: Record<string, unknown>;
|
|
53
|
+
callId: string;
|
|
54
|
+
step: number;
|
|
55
|
+
trace: TraceContext;
|
|
56
|
+
}
|
|
57
|
+
/** A tool-input guardrail decision. Unlike message-level guardrails it does NOT
|
|
58
|
+
* halt the run — a trip denies just this one tool call. */
|
|
59
|
+
export type ToolInputGuardrailDecision = {
|
|
60
|
+
pass: true;
|
|
61
|
+
} | {
|
|
62
|
+
pass: false;
|
|
63
|
+
reason: string;
|
|
64
|
+
};
|
|
65
|
+
/** Validates a tool call's arguments BEFORE it executes (and before any HITL
|
|
66
|
+
* approval interruption). On a trip the call is denied — the model receives the
|
|
67
|
+
* denial reason as an error tool result; the run continues and the approver is
|
|
68
|
+
* never consulted. Arguments are immutable once the model emits them, so a single
|
|
69
|
+
* pre-execution check is sufficient. */
|
|
70
|
+
export interface ToolInputGuardrail {
|
|
71
|
+
name: string;
|
|
72
|
+
check(ctx: ToolInputGuardrailContext): Promise<ToolInputGuardrailDecision> | ToolInputGuardrailDecision;
|
|
73
|
+
}
|
|
49
74
|
export interface GuardrailTriggeredContext {
|
|
50
75
|
runId: string;
|
|
51
76
|
agentId: string;
|
|
@@ -5,7 +5,7 @@ import type { CacheConfig, ThinkingConfig } from '../llm/types/request';
|
|
|
5
5
|
import type { ConversationHistory } from './history';
|
|
6
6
|
import type { HistorySnapshot } from './history-types';
|
|
7
7
|
import type { AgentTool } from './types';
|
|
8
|
-
import type { Guardrail } from './guardrail-types';
|
|
8
|
+
import type { Guardrail, ToolInputGuardrail } from './guardrail-types';
|
|
9
9
|
import type { PermissionPolicy } from '../plugins/permissions/policy';
|
|
10
10
|
import type { ApprovalRequest, ApprovalDecision } from './approval-types';
|
|
11
11
|
import type { Persistence } from '../plugins/persistence/types';
|
|
@@ -48,6 +48,10 @@ export interface AgentLoopConfig {
|
|
|
48
48
|
* output guardrails run after each step's response is produced.
|
|
49
49
|
* A tripwire decision halts the run with finishReason 'guardrail'. */
|
|
50
50
|
guardrails?: Guardrail[];
|
|
51
|
+
/** Per-tool-call input guardrails. Each runs against a tool call's arguments
|
|
52
|
+
* BEFORE the permission/approval check and execution. A trip denies just that
|
|
53
|
+
* call (error result to the model) without halting the run or invoking `approve`. */
|
|
54
|
+
toolInputGuardrails?: ToolInputGuardrail[];
|
|
51
55
|
/** Permission policy wired into the tool-execution path.
|
|
52
56
|
* Called after lookup, before execution.
|
|
53
57
|
* 'allow' -> proceed; 'deny' -> tool is blocked (error result to model);
|
package/dist/agent/loop.d.ts
CHANGED
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
import { HookBus } from '../bus/hook-bus';
|
|
14
14
|
import { type ContentPart, type Message } from '../llm/types/messages';
|
|
15
15
|
import type { ExecuteOptions } from '../llm/types/options';
|
|
16
|
-
import { type CompletionResponse } from '../llm/types/response';
|
|
16
|
+
import { type CompletionResponse, type FileOutput } from '../llm/types/response';
|
|
17
|
+
import type { FileStream, RetrievedFile } from '../llm/files/retrieve';
|
|
17
18
|
import type { LLMClient } from '../llm/client';
|
|
18
19
|
import { ConversationHistory } from './history';
|
|
19
20
|
import type { AgentLoopSnapshot, AgentRunReport, AgentStreamEvent, AgentTool } from './types';
|
|
@@ -39,6 +40,7 @@ export declare class AgentLoop {
|
|
|
39
40
|
private _toolTimeout;
|
|
40
41
|
private _maxSteps;
|
|
41
42
|
private _guardrails;
|
|
43
|
+
private _toolInputGuardrails;
|
|
42
44
|
private _policy;
|
|
43
45
|
private _approve;
|
|
44
46
|
private _checkpoint;
|
|
@@ -51,6 +53,14 @@ export declare class AgentLoop {
|
|
|
51
53
|
destroy(): void;
|
|
52
54
|
/** Model is owned by client. */
|
|
53
55
|
get model(): string;
|
|
56
|
+
/** Fetch a hosted-tool output file from the run's `response.files` — its bytes
|
|
57
|
+
* as a `Blob` plus `name` / `mimeType` / `size`. Delegates to the underlying
|
|
58
|
+
* client, so it uses this agent's provider + key (same as `retrieveFile` on a
|
|
59
|
+
* `complete()` result). */
|
|
60
|
+
retrieveFile(file: FileOutput): Promise<RetrievedFile>;
|
|
61
|
+
/** Stream a hosted-tool output file (large files) — a `ReadableStream` plus
|
|
62
|
+
* best-effort `name` / `mimeType` / `size`. Delegates to the underlying client. */
|
|
63
|
+
streamFile(file: FileOutput): Promise<FileStream>;
|
|
54
64
|
get system(): string;
|
|
55
65
|
set system(v: string);
|
|
56
66
|
get context(): string;
|
package/dist/agent/types.d.ts
CHANGED
|
@@ -39,6 +39,11 @@ export interface AgentTool {
|
|
|
39
39
|
definition: Tool;
|
|
40
40
|
/** Execute the tool. Return string or structured content. */
|
|
41
41
|
execute: (args: Record<string, unknown>, context: ToolExecutionContext) => Promise<string | ContentPart[]>;
|
|
42
|
+
/** Optional: derive out-of-band metadata from a successful tool result, attached
|
|
43
|
+
* to this call's `ToolCallReport.customData`. The MODEL NEVER SEES IT — it is for
|
|
44
|
+
* your own telemetry/routing/audit. Runs after `execute`; may be async. Errors are
|
|
45
|
+
* swallowed (opt-in convenience must never break the tool result). */
|
|
46
|
+
customDataExtractor?: (result: string | ContentPart[], args: Record<string, unknown>, context: Omit<ToolExecutionContext, 'signal'>) => unknown | Promise<unknown>;
|
|
42
47
|
}
|
|
43
48
|
export interface ToolExecutionContext {
|
|
44
49
|
step: number;
|
|
@@ -65,6 +70,9 @@ export interface ToolCallReport {
|
|
|
65
70
|
value: number | string | boolean;
|
|
66
71
|
type: string;
|
|
67
72
|
}>;
|
|
73
|
+
/** Out-of-band metadata from the tool's `customDataExtractor`, if any. Never sent
|
|
74
|
+
* to the model; present only when an extractor returned a value. */
|
|
75
|
+
customData?: unknown;
|
|
68
76
|
}
|
|
69
77
|
export interface StepReport {
|
|
70
78
|
index: number;
|
|
@@ -18,7 +18,8 @@ import type { AgentTool } from '../agent/types';
|
|
|
18
18
|
import type { LLMClientConfig } from '../llm/client-config';
|
|
19
19
|
import type { AudioOptions } from '../llm/types/audio';
|
|
20
20
|
import type { ContentPart, Message } from '../llm/types/messages';
|
|
21
|
-
import type { CompletionResponse } from '../llm/types/response';
|
|
21
|
+
import type { CompletionResponse, FileOutput } from '../llm/types/response';
|
|
22
|
+
import type { FileStream, RetrievedFile } from '../llm/files/retrieve';
|
|
22
23
|
import type { ProviderName } from '../llm/types/provider';
|
|
23
24
|
import type { ServiceTier } from '../llm/types/tiers';
|
|
24
25
|
import type { BuiltinTool } from '../llm/types/tools';
|
|
@@ -82,5 +83,11 @@ export interface CompleteResult<T = unknown> {
|
|
|
82
83
|
* otherwise `undefined`. The generic on `complete<T>(...)` types this. */
|
|
83
84
|
parsed?: T;
|
|
84
85
|
response: CompletionResponse;
|
|
86
|
+
/** Fetch a hosted-tool output file (from `response.files`): bytes (`Blob`) +
|
|
87
|
+
* `name` / `mimeType` / `size` — bound to the SAME model + key this call used. */
|
|
88
|
+
retrieveFile(file: FileOutput): Promise<RetrievedFile>;
|
|
89
|
+
/** Stream a hosted-tool output file (`ReadableStream` + `name` / `mimeType` /
|
|
90
|
+
* `size`) — for large files piped straight to a sink. Same model + key. */
|
|
91
|
+
streamFile(file: FileOutput): Promise<FileStream>;
|
|
85
92
|
}
|
|
86
93
|
export declare function complete<T = unknown>(opts: CompleteOptions): Promise<CompleteResult<T>>;
|