@spectyra/sdk 0.1.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/README.md +244 -0
- package/dist/adapters/claudeAgent.d.ts +10 -0
- package/dist/adapters/claudeAgent.js +29 -0
- package/dist/createSpectyra.d.ts +50 -0
- package/dist/createSpectyra.js +103 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +33 -0
- package/dist/legacy/SpectyraClient.d.ts +41 -0
- package/dist/legacy/SpectyraClient.js +85 -0
- package/dist/local/decideAgent.d.ts +16 -0
- package/dist/local/decideAgent.js +70 -0
- package/dist/remote/agentRemote.d.ts +14 -0
- package/dist/remote/agentRemote.js +47 -0
- package/dist/remote/chatRemote.d.ts +16 -0
- package/dist/remote/chatRemote.js +36 -0
- package/dist/remote/http.d.ts +13 -0
- package/dist/remote/http.js +37 -0
- package/dist/types.d.ts +223 -0
- package/dist/types.js +6 -0
- package/package.json +38 -0
package/README.md
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
# Spectyra SDK
|
|
2
|
+
|
|
3
|
+
**SDK-first agent runtime control: routing, budgets, tool gating, telemetry**
|
|
4
|
+
|
|
5
|
+
Spectyra SDK provides agent runtime control for Claude Agent SDK and other agent frameworks. Control model selection, budgets, tool permissions, and telemetry—all without requiring a proxy.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @spectyra/sdk
|
|
11
|
+
# or
|
|
12
|
+
pnpm add @spectyra/sdk
|
|
13
|
+
# or
|
|
14
|
+
yarn add @spectyra/sdk
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Two Integration Styles
|
|
18
|
+
|
|
19
|
+
### A) Local SDK Mode (Default)
|
|
20
|
+
|
|
21
|
+
**No proxy required.** SDK makes local decisions about agent options.
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import { createSpectyra } from '@spectyra/sdk';
|
|
25
|
+
|
|
26
|
+
// Local mode - works offline, no API calls
|
|
27
|
+
const spectyra = createSpectyra({ mode: "local" });
|
|
28
|
+
|
|
29
|
+
// One line integration with Claude Agent SDK
|
|
30
|
+
const options = spectyra.agentOptions(ctx, prompt);
|
|
31
|
+
const result = await agent.query({ prompt, options });
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### B) API Control Plane Mode (Enterprise)
|
|
35
|
+
|
|
36
|
+
SDK calls Spectyra API to fetch agent options and stream events for telemetry.
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import { createSpectyra } from '@spectyra/sdk';
|
|
40
|
+
|
|
41
|
+
const spectyra = createSpectyra({
|
|
42
|
+
mode: "api",
|
|
43
|
+
endpoint: "https://spectyra.up.railway.app/v1",
|
|
44
|
+
apiKey: process.env.SPECTYRA_API_KEY,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Fetch options from remote API
|
|
48
|
+
const response = await spectyra.agentOptionsRemote(ctx, promptMeta);
|
|
49
|
+
const result = await agent.query({ prompt, options: response.options });
|
|
50
|
+
|
|
51
|
+
// Stream events for telemetry
|
|
52
|
+
for await (const event of agentStream) {
|
|
53
|
+
await spectyra.sendAgentEvent(ctx, event);
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Quick Start: Local Mode
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { createSpectyra } from '@spectyra/sdk';
|
|
61
|
+
import { Agent } from '@anthropic-ai/sdk/agent';
|
|
62
|
+
|
|
63
|
+
// Create Spectyra instance (local mode - default)
|
|
64
|
+
const spectyra = createSpectyra({ mode: "local" });
|
|
65
|
+
|
|
66
|
+
// Create context for this agent run
|
|
67
|
+
const ctx = {
|
|
68
|
+
runId: crypto.randomUUID(),
|
|
69
|
+
budgetUsd: 2.5,
|
|
70
|
+
tags: { project: "my-app" },
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Get agent options (synchronous, local decision)
|
|
74
|
+
const prompt = "Fix the bug in src/utils.ts";
|
|
75
|
+
const options = spectyra.agentOptions(ctx, prompt);
|
|
76
|
+
|
|
77
|
+
// Use with Claude Agent SDK
|
|
78
|
+
const agent = new Agent({
|
|
79
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
80
|
+
...options, // Model, budget, tools, permissions
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
const result = await agent.query({ prompt });
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Quick Start: API Mode
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
import { createSpectyra } from '@spectyra/sdk';
|
|
90
|
+
|
|
91
|
+
const spectyra = createSpectyra({
|
|
92
|
+
mode: "api",
|
|
93
|
+
endpoint: "https://spectyra.up.railway.app/v1",
|
|
94
|
+
apiKey: process.env.SPECTYRA_API_KEY,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const ctx = {
|
|
98
|
+
runId: crypto.randomUUID(),
|
|
99
|
+
budgetUsd: 5.0,
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// Fetch options from remote API
|
|
103
|
+
const promptMeta = {
|
|
104
|
+
promptChars: prompt.length,
|
|
105
|
+
path: "code",
|
|
106
|
+
repoId: "my-repo",
|
|
107
|
+
language: "typescript",
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const response = await spectyra.agentOptionsRemote(ctx, promptMeta);
|
|
111
|
+
// response.run_id is set automatically
|
|
112
|
+
|
|
113
|
+
// Use options with agent
|
|
114
|
+
const agent = new Agent({
|
|
115
|
+
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
116
|
+
...response.options,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Stream events for telemetry
|
|
120
|
+
const stream = agent.queryStream({ prompt });
|
|
121
|
+
await spectyra.observeAgentStream(ctx, stream);
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## API Reference
|
|
125
|
+
|
|
126
|
+
### `createSpectyra(config?: SpectyraConfig)`
|
|
127
|
+
|
|
128
|
+
Create a Spectyra SDK instance.
|
|
129
|
+
|
|
130
|
+
**Config:**
|
|
131
|
+
- `mode?: "local" | "api"` - Default: `"local"`
|
|
132
|
+
- `endpoint?: string` - Required for API mode
|
|
133
|
+
- `apiKey?: string` - Required for API mode
|
|
134
|
+
- `defaults?: { budgetUsd?: number; models?: { small?, medium?, large? } }`
|
|
135
|
+
|
|
136
|
+
**Returns:** `SpectyraInstance`
|
|
137
|
+
|
|
138
|
+
### `agentOptions(ctx: SpectyraCtx, prompt: string | PromptMeta): ClaudeAgentOptions`
|
|
139
|
+
|
|
140
|
+
Get agent options locally (synchronous, offline).
|
|
141
|
+
|
|
142
|
+
**Context:**
|
|
143
|
+
- `runId?: string` - Run identifier
|
|
144
|
+
- `budgetUsd?: number` - Budget for this run
|
|
145
|
+
- `tags?: Record<string, string>` - Tags for analytics
|
|
146
|
+
|
|
147
|
+
**Returns:** Claude Agent SDK-compatible options
|
|
148
|
+
|
|
149
|
+
### `agentOptionsRemote(ctx: SpectyraCtx, promptMeta: PromptMeta): Promise<AgentOptionsResponse>`
|
|
150
|
+
|
|
151
|
+
Fetch agent options from remote API (asynchronous).
|
|
152
|
+
|
|
153
|
+
**PromptMeta:**
|
|
154
|
+
- `promptChars: number` - Prompt character count
|
|
155
|
+
- `path?: "code" | "talk"` - Path type
|
|
156
|
+
- `repoId?: string` - Repository identifier
|
|
157
|
+
- `language?: string` - Programming language
|
|
158
|
+
- `filesChanged?: number` - Number of files changed
|
|
159
|
+
|
|
160
|
+
**Returns:** Options with `run_id` and `reasons`
|
|
161
|
+
|
|
162
|
+
### `sendAgentEvent(ctx: SpectyraCtx, event: any): Promise<void>`
|
|
163
|
+
|
|
164
|
+
Send agent event for telemetry (best-effort, non-blocking).
|
|
165
|
+
|
|
166
|
+
### `observeAgentStream(ctx: SpectyraCtx, stream: AsyncIterable<any>): Promise<void>`
|
|
167
|
+
|
|
168
|
+
Observe agent stream and forward events automatically.
|
|
169
|
+
|
|
170
|
+
## Agent Options
|
|
171
|
+
|
|
172
|
+
The SDK returns Claude Agent SDK-compatible options:
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
interface ClaudeAgentOptions {
|
|
176
|
+
model?: string; // e.g., "claude-3-5-sonnet-latest"
|
|
177
|
+
maxBudgetUsd?: number; // Budget limit
|
|
178
|
+
allowedTools?: string[]; // e.g., ["Read", "Edit", "Bash"]
|
|
179
|
+
permissionMode?: "acceptEdits"; // Permission mode
|
|
180
|
+
canUseTool?: (tool, input) => boolean; // Tool gate function
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
## Local Decision Logic
|
|
185
|
+
|
|
186
|
+
In local mode, the SDK uses simple heuristics:
|
|
187
|
+
|
|
188
|
+
- **Prompt length < 6k chars** → Small tier → `claude-3-5-haiku-latest`
|
|
189
|
+
- **Prompt length < 20k chars** → Medium tier → `claude-3-5-sonnet-latest`
|
|
190
|
+
- **Prompt length ≥ 20k chars** → Large tier → `claude-3-7-sonnet-latest`
|
|
191
|
+
|
|
192
|
+
Default budget: $2.5 per run
|
|
193
|
+
Default tools: `["Read", "Edit", "Bash", "Glob"]`
|
|
194
|
+
Default permissions: `"acceptEdits"`
|
|
195
|
+
|
|
196
|
+
## Tool Gating
|
|
197
|
+
|
|
198
|
+
The SDK includes a default `canUseTool` gate that:
|
|
199
|
+
- ✅ Allows: Read, Edit, Bash (safe commands), Glob
|
|
200
|
+
- ❌ Denies: Bash commands containing `curl`, `wget`, `ssh`, `scp`, `nc`, `telnet`
|
|
201
|
+
|
|
202
|
+
You can override this by providing your own `canUseTool` function in the options.
|
|
203
|
+
|
|
204
|
+
## Remote Chat Optimization (Optional)
|
|
205
|
+
|
|
206
|
+
For chat optimization (not agentic), use the legacy client:
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
import { SpectyraClient } from '@spectyra/sdk';
|
|
210
|
+
|
|
211
|
+
const client = new SpectyraClient({
|
|
212
|
+
apiUrl: 'https://spectyra.up.railway.app/v1',
|
|
213
|
+
spectyraKey: process.env.SPECTYRA_API_KEY,
|
|
214
|
+
provider: 'openai',
|
|
215
|
+
providerKey: process.env.OPENAI_API_KEY, // BYOK
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
const response = await client.chat({
|
|
219
|
+
model: 'gpt-4o-mini',
|
|
220
|
+
messages: [{ role: 'user', content: 'Hello' }],
|
|
221
|
+
path: 'talk',
|
|
222
|
+
optimization_level: 3,
|
|
223
|
+
});
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**Note:** `SpectyraClient` is deprecated. For agentic use cases, use `createSpectyra()`.
|
|
227
|
+
|
|
228
|
+
## Examples
|
|
229
|
+
|
|
230
|
+
See `examples/` directory:
|
|
231
|
+
- `claude-agent-local.ts` - Local mode with Claude Agent SDK
|
|
232
|
+
- `claude-agent-remote.ts` - API mode with telemetry
|
|
233
|
+
- `chat-remote.ts` - Chat optimization (legacy)
|
|
234
|
+
|
|
235
|
+
## BYOK (Bring Your Own Key)
|
|
236
|
+
|
|
237
|
+
- Provider API keys are **never stored** server-side
|
|
238
|
+
- Keys are only used for the duration of the request
|
|
239
|
+
- You maintain full control over provider billing
|
|
240
|
+
- Agent options/events endpoints don't require provider keys
|
|
241
|
+
|
|
242
|
+
## License
|
|
243
|
+
|
|
244
|
+
MIT
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Agent SDK Adapter
|
|
3
|
+
*
|
|
4
|
+
* Converts Spectyra decisions to Claude Agent SDK-compatible options
|
|
5
|
+
*/
|
|
6
|
+
import type { AgentDecision, ClaudeAgentOptions } from "../types.js";
|
|
7
|
+
/**
|
|
8
|
+
* Convert agent decision to Claude Agent SDK options
|
|
9
|
+
*/
|
|
10
|
+
export declare function toClaudeAgentOptions(decision: AgentDecision): ClaudeAgentOptions;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Agent SDK Adapter
|
|
3
|
+
*
|
|
4
|
+
* Converts Spectyra decisions to Claude Agent SDK-compatible options
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Convert agent decision to Claude Agent SDK options
|
|
8
|
+
*/
|
|
9
|
+
export function toClaudeAgentOptions(decision) {
|
|
10
|
+
const options = {
|
|
11
|
+
...decision.options,
|
|
12
|
+
};
|
|
13
|
+
// Add default canUseTool gate if not provided
|
|
14
|
+
if (!options.canUseTool) {
|
|
15
|
+
options.canUseTool = (toolName, toolInput) => {
|
|
16
|
+
// Deny potentially dangerous Bash commands
|
|
17
|
+
if (toolName === "Bash") {
|
|
18
|
+
const command = typeof toolInput === "string" ? toolInput : toolInput?.command || "";
|
|
19
|
+
const dangerous = ["curl", "wget", "ssh", "scp", "nc", "telnet"];
|
|
20
|
+
const hasDangerous = dangerous.some(cmd => command.toLowerCase().includes(cmd));
|
|
21
|
+
if (hasDangerous) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return true;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
return options;
|
|
29
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create Spectyra SDK Instance
|
|
3
|
+
*
|
|
4
|
+
* Main entry point for SDK-first agentic integration
|
|
5
|
+
*/
|
|
6
|
+
import type { SpectyraConfig, SpectyraCtx, PromptMeta, ClaudeAgentOptions, AgentOptionsResponse, ChatOptions, ChatResponse } from "./types.js";
|
|
7
|
+
export interface SpectyraInstance {
|
|
8
|
+
/**
|
|
9
|
+
* Get agent options locally (SDK mode - default)
|
|
10
|
+
* Synchronous, works offline, no API calls
|
|
11
|
+
*/
|
|
12
|
+
agentOptions(ctx: SpectyraCtx, prompt: string | PromptMeta): ClaudeAgentOptions;
|
|
13
|
+
/**
|
|
14
|
+
* Get agent options from remote API (API mode)
|
|
15
|
+
* Asynchronous, requires endpoint and apiKey
|
|
16
|
+
*/
|
|
17
|
+
agentOptionsRemote(ctx: SpectyraCtx, promptMeta: PromptMeta): Promise<AgentOptionsResponse>;
|
|
18
|
+
/**
|
|
19
|
+
* Send agent event to remote API
|
|
20
|
+
*/
|
|
21
|
+
sendAgentEvent(ctx: SpectyraCtx, event: any): Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* Observe agent stream and forward events
|
|
24
|
+
*/
|
|
25
|
+
observeAgentStream(ctx: SpectyraCtx, stream: AsyncIterable<any>): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Chat optimization (remote API mode)
|
|
28
|
+
* Optional wrapper for /v1/chat endpoint
|
|
29
|
+
*/
|
|
30
|
+
chatRemote?(options: ChatOptions): Promise<ChatResponse>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Create a Spectyra SDK instance
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* // Local mode (default, no API required)
|
|
38
|
+
* const spectyra = createSpectyra({ mode: "local" });
|
|
39
|
+
* const options = spectyra.agentOptions(ctx, prompt);
|
|
40
|
+
*
|
|
41
|
+
* // API mode (enterprise control plane)
|
|
42
|
+
* const spectyra = createSpectyra({
|
|
43
|
+
* mode: "api",
|
|
44
|
+
* endpoint: "https://spectyra.up.railway.app/v1",
|
|
45
|
+
* apiKey: process.env.SPECTYRA_API_KEY,
|
|
46
|
+
* });
|
|
47
|
+
* const response = await spectyra.agentOptionsRemote(ctx, promptMeta);
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export declare function createSpectyra(config?: SpectyraConfig): SpectyraInstance;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Create Spectyra SDK Instance
|
|
3
|
+
*
|
|
4
|
+
* Main entry point for SDK-first agentic integration
|
|
5
|
+
*/
|
|
6
|
+
import { decideAgent } from "./local/decideAgent.js";
|
|
7
|
+
import { toClaudeAgentOptions } from "./adapters/claudeAgent.js";
|
|
8
|
+
import { fetchAgentOptions, sendAgentEvent } from "./remote/agentRemote.js";
|
|
9
|
+
/**
|
|
10
|
+
* Create a Spectyra SDK instance
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* // Local mode (default, no API required)
|
|
15
|
+
* const spectyra = createSpectyra({ mode: "local" });
|
|
16
|
+
* const options = spectyra.agentOptions(ctx, prompt);
|
|
17
|
+
*
|
|
18
|
+
* // API mode (enterprise control plane)
|
|
19
|
+
* const spectyra = createSpectyra({
|
|
20
|
+
* mode: "api",
|
|
21
|
+
* endpoint: "https://spectyra.up.railway.app/v1",
|
|
22
|
+
* apiKey: process.env.SPECTYRA_API_KEY,
|
|
23
|
+
* });
|
|
24
|
+
* const response = await spectyra.agentOptionsRemote(ctx, promptMeta);
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export function createSpectyra(config = {}) {
|
|
28
|
+
const mode = config.mode || "local";
|
|
29
|
+
const endpoint = config.endpoint;
|
|
30
|
+
const apiKey = config.apiKey;
|
|
31
|
+
// Validate API mode requirements
|
|
32
|
+
if (mode === "api") {
|
|
33
|
+
if (!endpoint) {
|
|
34
|
+
throw new Error("endpoint is required for API mode");
|
|
35
|
+
}
|
|
36
|
+
if (!apiKey) {
|
|
37
|
+
throw new Error("apiKey is required for API mode");
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
/**
|
|
42
|
+
* Local agent options (synchronous, offline)
|
|
43
|
+
*/
|
|
44
|
+
agentOptions(ctx, prompt) {
|
|
45
|
+
const decision = decideAgent({ config, ctx, prompt });
|
|
46
|
+
return toClaudeAgentOptions(decision);
|
|
47
|
+
},
|
|
48
|
+
/**
|
|
49
|
+
* Remote agent options (asynchronous, requires API)
|
|
50
|
+
*/
|
|
51
|
+
async agentOptionsRemote(ctx, promptMeta) {
|
|
52
|
+
if (mode !== "api" || !endpoint || !apiKey) {
|
|
53
|
+
throw new Error("agentOptionsRemote requires API mode with endpoint and apiKey");
|
|
54
|
+
}
|
|
55
|
+
const response = await fetchAgentOptions(endpoint, apiKey, ctx, promptMeta);
|
|
56
|
+
// Update ctx with run_id if returned
|
|
57
|
+
if (response.run_id && !ctx.runId) {
|
|
58
|
+
ctx.runId = response.run_id;
|
|
59
|
+
}
|
|
60
|
+
return response;
|
|
61
|
+
},
|
|
62
|
+
/**
|
|
63
|
+
* Send agent event
|
|
64
|
+
*/
|
|
65
|
+
async sendAgentEvent(ctx, event) {
|
|
66
|
+
if (mode !== "api" || !endpoint || !apiKey) {
|
|
67
|
+
// In local mode, events are no-ops (best effort)
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
await sendAgentEvent(endpoint, apiKey, ctx, event);
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
// Best-effort: don't throw, just log if possible
|
|
75
|
+
console.warn("Failed to send agent event:", error);
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
/**
|
|
79
|
+
* Observe agent stream and forward events
|
|
80
|
+
*/
|
|
81
|
+
async observeAgentStream(ctx, stream) {
|
|
82
|
+
try {
|
|
83
|
+
for await (const event of stream) {
|
|
84
|
+
await this.sendAgentEvent(ctx, event);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
// Best-effort: don't throw
|
|
89
|
+
console.warn("Error observing agent stream:", error);
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
/**
|
|
93
|
+
* Chat remote (optional, for backwards compatibility)
|
|
94
|
+
*/
|
|
95
|
+
chatRemote: mode === "api" && endpoint && apiKey
|
|
96
|
+
? async (options) => {
|
|
97
|
+
// This requires provider and providerKey which aren't in config
|
|
98
|
+
// So this is only available if user provides them separately
|
|
99
|
+
throw new Error("chatRemote requires provider and providerKey. Use legacy SpectyraClient for chat optimization.");
|
|
100
|
+
}
|
|
101
|
+
: undefined,
|
|
102
|
+
};
|
|
103
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spectyra SDK
|
|
3
|
+
*
|
|
4
|
+
* SDK-first agent runtime control: routing, budgets, tool gating, telemetry
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* // Local mode (default, no API required)
|
|
9
|
+
* import { createSpectyra } from '@spectyra/sdk';
|
|
10
|
+
*
|
|
11
|
+
* const spectyra = createSpectyra({ mode: "local" });
|
|
12
|
+
*
|
|
13
|
+
* // Use with Claude Agent SDK
|
|
14
|
+
* const options = spectyra.agentOptions(ctx, prompt);
|
|
15
|
+
* const result = await agent.query({ prompt, options });
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* // API mode (enterprise control plane)
|
|
21
|
+
* const spectyra = createSpectyra({
|
|
22
|
+
* mode: "api",
|
|
23
|
+
* endpoint: "https://spectyra.up.railway.app/v1",
|
|
24
|
+
* apiKey: process.env.SPECTYRA_API_KEY,
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* const response = await spectyra.agentOptionsRemote(ctx, promptMeta);
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export { createSpectyra } from "./createSpectyra.js";
|
|
31
|
+
export type { SpectyraConfig, SpectyraMode, SpectyraCtx, PromptMeta, ClaudeAgentOptions, AgentDecision, AgentOptionsRequest, AgentOptionsResponse, AgentEventRequest, AgentEventResponse, } from "./types.js";
|
|
32
|
+
export { SpectyraClient } from "./legacy/SpectyraClient.js";
|
|
33
|
+
export type { SpectyraClientConfig, ChatOptions, ChatResponse, ChatMessage, Usage, Path, Mode, } from "./types.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spectyra SDK
|
|
3
|
+
*
|
|
4
|
+
* SDK-first agent runtime control: routing, budgets, tool gating, telemetry
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* // Local mode (default, no API required)
|
|
9
|
+
* import { createSpectyra } from '@spectyra/sdk';
|
|
10
|
+
*
|
|
11
|
+
* const spectyra = createSpectyra({ mode: "local" });
|
|
12
|
+
*
|
|
13
|
+
* // Use with Claude Agent SDK
|
|
14
|
+
* const options = spectyra.agentOptions(ctx, prompt);
|
|
15
|
+
* const result = await agent.query({ prompt, options });
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* // API mode (enterprise control plane)
|
|
21
|
+
* const spectyra = createSpectyra({
|
|
22
|
+
* mode: "api",
|
|
23
|
+
* endpoint: "https://spectyra.up.railway.app/v1",
|
|
24
|
+
* apiKey: process.env.SPECTYRA_API_KEY,
|
|
25
|
+
* });
|
|
26
|
+
*
|
|
27
|
+
* const response = await spectyra.agentOptionsRemote(ctx, promptMeta);
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
// New SDK-first API
|
|
31
|
+
export { createSpectyra } from "./createSpectyra.js";
|
|
32
|
+
// Legacy API (deprecated but still supported)
|
|
33
|
+
export { SpectyraClient } from "./legacy/SpectyraClient.js";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Legacy Spectyra Client
|
|
3
|
+
*
|
|
4
|
+
* @deprecated Use createSpectyra({mode:"api"}).chatRemote(...) or createSpectyra({mode:"local"})
|
|
5
|
+
*
|
|
6
|
+
* This class is maintained for backwards compatibility.
|
|
7
|
+
* It now uses the new remote chat client internally.
|
|
8
|
+
*/
|
|
9
|
+
import type { SpectyraClientConfig, ChatOptions, ChatResponse } from "../types.js";
|
|
10
|
+
/**
|
|
11
|
+
* @deprecated Use createSpectyra({mode:"api"}) instead
|
|
12
|
+
*
|
|
13
|
+
* Legacy client for chat optimization via API.
|
|
14
|
+
* For agentic use cases, use createSpectyra() with agentOptions().
|
|
15
|
+
*/
|
|
16
|
+
export declare class SpectyraClient {
|
|
17
|
+
private config;
|
|
18
|
+
constructor(config: SpectyraClientConfig);
|
|
19
|
+
/**
|
|
20
|
+
* Send a chat request through Spectyra optimization.
|
|
21
|
+
*
|
|
22
|
+
* @deprecated Use createSpectyra({mode:"api"}).chatRemote() instead
|
|
23
|
+
*
|
|
24
|
+
* @param options Chat options
|
|
25
|
+
* @returns Optimized response with savings metrics
|
|
26
|
+
*/
|
|
27
|
+
chat(options: ChatOptions): Promise<ChatResponse>;
|
|
28
|
+
/**
|
|
29
|
+
* Estimate savings for a conversation without making real LLM calls.
|
|
30
|
+
*
|
|
31
|
+
* @deprecated Use createSpectyra({mode:"api"}).chatRemote() with dry_run option
|
|
32
|
+
*
|
|
33
|
+
* @param options Chat options (dry_run will be set to true)
|
|
34
|
+
* @returns Estimated savings
|
|
35
|
+
*/
|
|
36
|
+
estimateSavings(options: Omit<ChatOptions, "dry_run">): Promise<ChatResponse>;
|
|
37
|
+
/**
|
|
38
|
+
* Get the current configuration (without sensitive keys)
|
|
39
|
+
*/
|
|
40
|
+
getConfig(): Omit<SpectyraClientConfig, "spectyraKey" | "providerKey">;
|
|
41
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Legacy Spectyra Client
|
|
3
|
+
*
|
|
4
|
+
* @deprecated Use createSpectyra({mode:"api"}).chatRemote(...) or createSpectyra({mode:"local"})
|
|
5
|
+
*
|
|
6
|
+
* This class is maintained for backwards compatibility.
|
|
7
|
+
* It now uses the new remote chat client internally.
|
|
8
|
+
*/
|
|
9
|
+
import { chatRemote } from "../remote/chatRemote.js";
|
|
10
|
+
/**
|
|
11
|
+
* @deprecated Use createSpectyra({mode:"api"}) instead
|
|
12
|
+
*
|
|
13
|
+
* Legacy client for chat optimization via API.
|
|
14
|
+
* For agentic use cases, use createSpectyra() with agentOptions().
|
|
15
|
+
*/
|
|
16
|
+
export class SpectyraClient {
|
|
17
|
+
config;
|
|
18
|
+
constructor(config) {
|
|
19
|
+
this.config = config;
|
|
20
|
+
if (!config.apiUrl) {
|
|
21
|
+
throw new Error("apiUrl is required");
|
|
22
|
+
}
|
|
23
|
+
if (!config.spectyraKey) {
|
|
24
|
+
throw new Error("spectyraKey is required");
|
|
25
|
+
}
|
|
26
|
+
if (!config.provider) {
|
|
27
|
+
throw new Error("provider is required");
|
|
28
|
+
}
|
|
29
|
+
if (!config.providerKey) {
|
|
30
|
+
throw new Error("providerKey is required (BYOK)");
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Send a chat request through Spectyra optimization.
|
|
35
|
+
*
|
|
36
|
+
* @deprecated Use createSpectyra({mode:"api"}).chatRemote() instead
|
|
37
|
+
*
|
|
38
|
+
* @param options Chat options
|
|
39
|
+
* @returns Optimized response with savings metrics
|
|
40
|
+
*/
|
|
41
|
+
async chat(options) {
|
|
42
|
+
const { model, messages, path, optimization_level = 2, conversation_id, dry_run = false, } = options;
|
|
43
|
+
// Validate messages
|
|
44
|
+
if (!messages || messages.length === 0) {
|
|
45
|
+
throw new Error("messages array cannot be empty");
|
|
46
|
+
}
|
|
47
|
+
// Use new remote chat client
|
|
48
|
+
return chatRemote({
|
|
49
|
+
endpoint: this.config.apiUrl,
|
|
50
|
+
apiKey: this.config.spectyraKey,
|
|
51
|
+
provider: this.config.provider,
|
|
52
|
+
providerKey: this.config.providerKey,
|
|
53
|
+
}, {
|
|
54
|
+
model,
|
|
55
|
+
messages,
|
|
56
|
+
path,
|
|
57
|
+
optimization_level,
|
|
58
|
+
conversation_id,
|
|
59
|
+
dry_run,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Estimate savings for a conversation without making real LLM calls.
|
|
64
|
+
*
|
|
65
|
+
* @deprecated Use createSpectyra({mode:"api"}).chatRemote() with dry_run option
|
|
66
|
+
*
|
|
67
|
+
* @param options Chat options (dry_run will be set to true)
|
|
68
|
+
* @returns Estimated savings
|
|
69
|
+
*/
|
|
70
|
+
async estimateSavings(options) {
|
|
71
|
+
return this.chat({
|
|
72
|
+
...options,
|
|
73
|
+
dry_run: true,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get the current configuration (without sensitive keys)
|
|
78
|
+
*/
|
|
79
|
+
getConfig() {
|
|
80
|
+
return {
|
|
81
|
+
apiUrl: this.config.apiUrl,
|
|
82
|
+
provider: this.config.provider,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local Agent Decision Engine
|
|
3
|
+
*
|
|
4
|
+
* Makes local decisions about agent options without requiring API calls.
|
|
5
|
+
* This is the default "SDK mode" - works offline.
|
|
6
|
+
*/
|
|
7
|
+
import type { SpectyraConfig, SpectyraCtx, PromptMeta, AgentDecision } from "../types.js";
|
|
8
|
+
export interface DecideAgentInput {
|
|
9
|
+
config: SpectyraConfig;
|
|
10
|
+
ctx: SpectyraCtx;
|
|
11
|
+
prompt: string | PromptMeta;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Determine agent options locally based on prompt characteristics
|
|
15
|
+
*/
|
|
16
|
+
export declare function decideAgent(input: DecideAgentInput): AgentDecision;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local Agent Decision Engine
|
|
3
|
+
*
|
|
4
|
+
* Makes local decisions about agent options without requiring API calls.
|
|
5
|
+
* This is the default "SDK mode" - works offline.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Determine agent options locally based on prompt characteristics
|
|
9
|
+
*/
|
|
10
|
+
export function decideAgent(input) {
|
|
11
|
+
const { config, ctx, prompt } = input;
|
|
12
|
+
const reasons = [];
|
|
13
|
+
// Determine prompt length
|
|
14
|
+
const promptLength = typeof prompt === "string" ? prompt.length : prompt.promptChars;
|
|
15
|
+
const path = typeof prompt === "string" ? undefined : prompt.path;
|
|
16
|
+
// Determine tier by prompt length
|
|
17
|
+
let tier;
|
|
18
|
+
if (promptLength < 6000) {
|
|
19
|
+
tier = "small";
|
|
20
|
+
reasons.push(`Prompt length ${promptLength} chars → small tier`);
|
|
21
|
+
}
|
|
22
|
+
else if (promptLength < 20000) {
|
|
23
|
+
tier = "medium";
|
|
24
|
+
reasons.push(`Prompt length ${promptLength} chars → medium tier`);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
tier = "large";
|
|
28
|
+
reasons.push(`Prompt length ${promptLength} chars → large tier`);
|
|
29
|
+
}
|
|
30
|
+
// Choose model from config or defaults
|
|
31
|
+
const modelDefaults = config.defaults?.models || {};
|
|
32
|
+
let model;
|
|
33
|
+
if (tier === "small") {
|
|
34
|
+
model = modelDefaults.small || "claude-3-5-haiku-latest";
|
|
35
|
+
reasons.push(`Small tier → ${model}`);
|
|
36
|
+
}
|
|
37
|
+
else if (tier === "medium") {
|
|
38
|
+
model = modelDefaults.medium || "claude-3-5-sonnet-latest";
|
|
39
|
+
reasons.push(`Medium tier → ${model}`);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
model = modelDefaults.large || "claude-3-7-sonnet-latest";
|
|
43
|
+
reasons.push(`Large tier → ${model}`);
|
|
44
|
+
}
|
|
45
|
+
// Set budget
|
|
46
|
+
const maxBudgetUsd = ctx.budgetUsd || config.defaults?.budgetUsd || 2.5;
|
|
47
|
+
reasons.push(`Budget: $${maxBudgetUsd} (from ctx or defaults)`);
|
|
48
|
+
// Default allowed tools (configurable)
|
|
49
|
+
const allowedTools = ["Read", "Edit", "Bash", "Glob"];
|
|
50
|
+
reasons.push(`Allowed tools: ${allowedTools.join(", ")}`);
|
|
51
|
+
// Permission mode
|
|
52
|
+
const permissionMode = "acceptEdits";
|
|
53
|
+
reasons.push(`Permission mode: ${permissionMode}`);
|
|
54
|
+
// Build options
|
|
55
|
+
const options = {
|
|
56
|
+
model,
|
|
57
|
+
maxBudgetUsd,
|
|
58
|
+
allowedTools,
|
|
59
|
+
permissionMode,
|
|
60
|
+
// canUseTool will be set by adapter if needed
|
|
61
|
+
};
|
|
62
|
+
// Add path-specific context if available
|
|
63
|
+
if (path) {
|
|
64
|
+
reasons.push(`Path: ${path}`);
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
options,
|
|
68
|
+
reasons,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote Agent Client
|
|
3
|
+
*
|
|
4
|
+
* Handles agent-related API calls (options, events)
|
|
5
|
+
*/
|
|
6
|
+
import type { SpectyraCtx, PromptMeta, AgentOptionsResponse, AgentEventResponse } from "../types.js";
|
|
7
|
+
/**
|
|
8
|
+
* Fetch agent options from remote API
|
|
9
|
+
*/
|
|
10
|
+
export declare function fetchAgentOptions(endpoint: string, apiKey: string, ctx: SpectyraCtx, promptMeta: PromptMeta): Promise<AgentOptionsResponse>;
|
|
11
|
+
/**
|
|
12
|
+
* Send agent event to remote API
|
|
13
|
+
*/
|
|
14
|
+
export declare function sendAgentEvent(endpoint: string, apiKey: string, ctx: SpectyraCtx, event: any): Promise<AgentEventResponse>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote Agent Client
|
|
3
|
+
*
|
|
4
|
+
* Handles agent-related API calls (options, events)
|
|
5
|
+
*/
|
|
6
|
+
import { postJson } from "./http.js";
|
|
7
|
+
/**
|
|
8
|
+
* Fetch agent options from remote API
|
|
9
|
+
*/
|
|
10
|
+
export async function fetchAgentOptions(endpoint, apiKey, ctx, promptMeta) {
|
|
11
|
+
const url = `${endpoint}/agent/options`;
|
|
12
|
+
const request = {
|
|
13
|
+
run_id: ctx.runId,
|
|
14
|
+
prompt_meta: promptMeta,
|
|
15
|
+
preferences: {
|
|
16
|
+
budgetUsd: ctx.budgetUsd,
|
|
17
|
+
// allowTools can be added if needed
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
return postJson(url, apiKey, request);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Send agent event to remote API
|
|
24
|
+
*/
|
|
25
|
+
export async function sendAgentEvent(endpoint, apiKey, ctx, event) {
|
|
26
|
+
if (!ctx.runId) {
|
|
27
|
+
throw new Error("runId is required to send agent events");
|
|
28
|
+
}
|
|
29
|
+
const url = `${endpoint}/agent/events`;
|
|
30
|
+
// Truncate large events (max 256KB JSON)
|
|
31
|
+
let eventToSend = event;
|
|
32
|
+
const eventJson = JSON.stringify(event);
|
|
33
|
+
if (eventJson.length > 256 * 1024) {
|
|
34
|
+
// Store only type + summary for large events
|
|
35
|
+
eventToSend = {
|
|
36
|
+
type: event.type || "unknown",
|
|
37
|
+
summary: "Event truncated (exceeds 256KB)",
|
|
38
|
+
originalSize: eventJson.length,
|
|
39
|
+
timestamp: new Date().toISOString(),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
const request = {
|
|
43
|
+
run_id: ctx.runId,
|
|
44
|
+
event: eventToSend,
|
|
45
|
+
};
|
|
46
|
+
return postJson(url, apiKey, request);
|
|
47
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote Chat Client
|
|
3
|
+
*
|
|
4
|
+
* Handles chat optimization API calls (backwards compatibility)
|
|
5
|
+
*/
|
|
6
|
+
import type { ChatOptions, ChatResponse } from "../types.js";
|
|
7
|
+
export interface ChatRemoteConfig {
|
|
8
|
+
endpoint: string;
|
|
9
|
+
apiKey: string;
|
|
10
|
+
provider: string;
|
|
11
|
+
providerKey: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Send chat request to Spectyra API for optimization
|
|
15
|
+
*/
|
|
16
|
+
export declare function chatRemote(config: ChatRemoteConfig, options: ChatOptions): Promise<ChatResponse>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote Chat Client
|
|
3
|
+
*
|
|
4
|
+
* Handles chat optimization API calls (backwards compatibility)
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Send chat request to Spectyra API for optimization
|
|
8
|
+
*/
|
|
9
|
+
export async function chatRemote(config, options) {
|
|
10
|
+
const url = `${config.endpoint}/chat`;
|
|
11
|
+
const body = {
|
|
12
|
+
path: options.path,
|
|
13
|
+
provider: config.provider,
|
|
14
|
+
model: options.model,
|
|
15
|
+
messages: options.messages,
|
|
16
|
+
mode: "optimized",
|
|
17
|
+
optimization_level: options.optimization_level ?? 2,
|
|
18
|
+
conversation_id: options.conversation_id,
|
|
19
|
+
dry_run: options.dry_run ?? false,
|
|
20
|
+
};
|
|
21
|
+
// Make request with both Spectyra key and provider key (BYOK)
|
|
22
|
+
const response = await fetch(url, {
|
|
23
|
+
method: "POST",
|
|
24
|
+
headers: {
|
|
25
|
+
"Content-Type": "application/json",
|
|
26
|
+
"X-SPECTYRA-API-KEY": config.apiKey,
|
|
27
|
+
"X-PROVIDER-KEY": config.providerKey, // BYOK - never stored server-side
|
|
28
|
+
},
|
|
29
|
+
body: JSON.stringify(body),
|
|
30
|
+
});
|
|
31
|
+
if (!response.ok) {
|
|
32
|
+
const error = await response.json().catch(() => ({ error: "Unknown error" }));
|
|
33
|
+
throw new Error(`Spectyra API error: ${error.error || response.statusText}`);
|
|
34
|
+
}
|
|
35
|
+
return response.json();
|
|
36
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote HTTP Client
|
|
3
|
+
*
|
|
4
|
+
* Handles HTTP requests to Spectyra API with proper error handling
|
|
5
|
+
*/
|
|
6
|
+
export interface ApiError {
|
|
7
|
+
error: string;
|
|
8
|
+
details?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Make a POST request to Spectyra API
|
|
12
|
+
*/
|
|
13
|
+
export declare function postJson<T>(url: string, apiKey: string, body: any): Promise<T>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote HTTP Client
|
|
3
|
+
*
|
|
4
|
+
* Handles HTTP requests to Spectyra API with proper error handling
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Make a POST request to Spectyra API
|
|
8
|
+
*/
|
|
9
|
+
export async function postJson(url, apiKey, body) {
|
|
10
|
+
const response = await fetch(url, {
|
|
11
|
+
method: "POST",
|
|
12
|
+
headers: {
|
|
13
|
+
"Content-Type": "application/json",
|
|
14
|
+
"X-SPECTYRA-API-KEY": apiKey,
|
|
15
|
+
},
|
|
16
|
+
body: JSON.stringify(body),
|
|
17
|
+
});
|
|
18
|
+
if (!response.ok) {
|
|
19
|
+
let errorMessage = `API error: ${response.statusText}`;
|
|
20
|
+
try {
|
|
21
|
+
const errorData = await response.json();
|
|
22
|
+
errorMessage = errorData.error || errorMessage;
|
|
23
|
+
if (errorData.details) {
|
|
24
|
+
errorMessage += ` - ${errorData.details}`;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// If JSON parsing fails, use status text
|
|
29
|
+
const text = await response.text();
|
|
30
|
+
if (text) {
|
|
31
|
+
errorMessage = text.substring(0, 200);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
throw new Error(errorMessage);
|
|
35
|
+
}
|
|
36
|
+
return response.json();
|
|
37
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spectyra SDK Types
|
|
3
|
+
*
|
|
4
|
+
* Core types for SDK-first agentic integration
|
|
5
|
+
*/
|
|
6
|
+
export type SpectyraMode = "local" | "api";
|
|
7
|
+
export interface SpectyraConfig {
|
|
8
|
+
/**
|
|
9
|
+
* SDK mode: "local" (default, no proxy) or "api" (remote control plane)
|
|
10
|
+
*/
|
|
11
|
+
mode?: SpectyraMode;
|
|
12
|
+
/**
|
|
13
|
+
* Spectyra API endpoint (required for "api" mode, optional for "local")
|
|
14
|
+
* Example: "https://spectyra.up.railway.app/v1"
|
|
15
|
+
*/
|
|
16
|
+
endpoint?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Spectyra API key (required for "api" mode, optional for "local")
|
|
19
|
+
*/
|
|
20
|
+
apiKey?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Default settings
|
|
23
|
+
*/
|
|
24
|
+
defaults?: {
|
|
25
|
+
/**
|
|
26
|
+
* Default budget in USD per agent run
|
|
27
|
+
*/
|
|
28
|
+
budgetUsd?: number;
|
|
29
|
+
/**
|
|
30
|
+
* Model preferences by tier
|
|
31
|
+
*/
|
|
32
|
+
models?: {
|
|
33
|
+
small?: string;
|
|
34
|
+
medium?: string;
|
|
35
|
+
large?: string;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export interface SpectyraCtx {
|
|
40
|
+
/**
|
|
41
|
+
* Organization ID (optional; in API mode server derives from API key)
|
|
42
|
+
*/
|
|
43
|
+
orgId?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Project ID (optional)
|
|
46
|
+
*/
|
|
47
|
+
projectId?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Run ID for tracking this agent session
|
|
50
|
+
*/
|
|
51
|
+
runId?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Budget in USD for this run
|
|
54
|
+
*/
|
|
55
|
+
budgetUsd?: number;
|
|
56
|
+
/**
|
|
57
|
+
* Tags for filtering/analytics
|
|
58
|
+
*/
|
|
59
|
+
tags?: Record<string, string>;
|
|
60
|
+
}
|
|
61
|
+
export interface PromptMeta {
|
|
62
|
+
/**
|
|
63
|
+
* Prompt character count (to avoid sending full prompt by default)
|
|
64
|
+
*/
|
|
65
|
+
promptChars: number;
|
|
66
|
+
/**
|
|
67
|
+
* Path: "code" for coding, "talk" for chat/Q&A
|
|
68
|
+
*/
|
|
69
|
+
path?: "code" | "talk";
|
|
70
|
+
/**
|
|
71
|
+
* Repository identifier (optional)
|
|
72
|
+
*/
|
|
73
|
+
repoId?: string;
|
|
74
|
+
/**
|
|
75
|
+
* Programming language (optional)
|
|
76
|
+
*/
|
|
77
|
+
language?: string;
|
|
78
|
+
/**
|
|
79
|
+
* Number of files changed (optional)
|
|
80
|
+
*/
|
|
81
|
+
filesChanged?: number;
|
|
82
|
+
/**
|
|
83
|
+
* Test command (optional)
|
|
84
|
+
*/
|
|
85
|
+
testCommand?: string;
|
|
86
|
+
}
|
|
87
|
+
export interface ClaudeAgentOptions {
|
|
88
|
+
/**
|
|
89
|
+
* Model name (e.g., "claude-3-5-sonnet-latest")
|
|
90
|
+
*/
|
|
91
|
+
model?: string;
|
|
92
|
+
/**
|
|
93
|
+
* Maximum budget in USD for this run
|
|
94
|
+
*/
|
|
95
|
+
maxBudgetUsd?: number;
|
|
96
|
+
/**
|
|
97
|
+
* Working directory
|
|
98
|
+
*/
|
|
99
|
+
cwd?: string;
|
|
100
|
+
/**
|
|
101
|
+
* Allowed tool names
|
|
102
|
+
*/
|
|
103
|
+
allowedTools?: string[];
|
|
104
|
+
/**
|
|
105
|
+
* Permission mode
|
|
106
|
+
*/
|
|
107
|
+
permissionMode?: "default" | "acceptEdits" | "bypassPermissions";
|
|
108
|
+
/**
|
|
109
|
+
* Tool usage gate function
|
|
110
|
+
*/
|
|
111
|
+
canUseTool?: (toolName: string, toolInput: any) => boolean | Promise<boolean>;
|
|
112
|
+
}
|
|
113
|
+
export interface AgentDecision {
|
|
114
|
+
/**
|
|
115
|
+
* Generated agent options
|
|
116
|
+
*/
|
|
117
|
+
options: ClaudeAgentOptions;
|
|
118
|
+
/**
|
|
119
|
+
* Decision reasons (for debugging)
|
|
120
|
+
*/
|
|
121
|
+
reasons: string[];
|
|
122
|
+
}
|
|
123
|
+
export interface AgentOptionsRequest {
|
|
124
|
+
run_id?: string;
|
|
125
|
+
prompt_meta: PromptMeta;
|
|
126
|
+
preferences?: {
|
|
127
|
+
budgetUsd?: number;
|
|
128
|
+
allowTools?: string[];
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
export interface AgentOptionsResponse {
|
|
132
|
+
run_id: string;
|
|
133
|
+
options: ClaudeAgentOptions;
|
|
134
|
+
reasons: string[];
|
|
135
|
+
}
|
|
136
|
+
export interface AgentEventRequest {
|
|
137
|
+
run_id: string;
|
|
138
|
+
event: any;
|
|
139
|
+
}
|
|
140
|
+
export interface AgentEventResponse {
|
|
141
|
+
ok: boolean;
|
|
142
|
+
}
|
|
143
|
+
export type Path = "talk" | "code";
|
|
144
|
+
export type Mode = "baseline" | "optimized";
|
|
145
|
+
export interface ChatMessage {
|
|
146
|
+
role: "system" | "user" | "assistant";
|
|
147
|
+
content: string;
|
|
148
|
+
}
|
|
149
|
+
export interface Usage {
|
|
150
|
+
input_tokens: number;
|
|
151
|
+
output_tokens: number;
|
|
152
|
+
total_tokens: number;
|
|
153
|
+
estimated?: boolean;
|
|
154
|
+
}
|
|
155
|
+
export interface ChatResponse {
|
|
156
|
+
id: string;
|
|
157
|
+
created_at: string;
|
|
158
|
+
mode: Mode;
|
|
159
|
+
path: Path;
|
|
160
|
+
optimization_level: number;
|
|
161
|
+
provider: string;
|
|
162
|
+
model: string;
|
|
163
|
+
response_text: string;
|
|
164
|
+
usage: Usage;
|
|
165
|
+
cost_usd: number;
|
|
166
|
+
savings?: {
|
|
167
|
+
savings_type: "verified" | "estimated" | "shadow_verified";
|
|
168
|
+
tokens_saved: number;
|
|
169
|
+
pct_saved: number;
|
|
170
|
+
cost_saved_usd: number;
|
|
171
|
+
confidence_band?: "high" | "medium" | "low";
|
|
172
|
+
};
|
|
173
|
+
quality?: {
|
|
174
|
+
pass: boolean;
|
|
175
|
+
failures: string[];
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
export interface SpectyraClientConfig {
|
|
179
|
+
/**
|
|
180
|
+
* Spectyra API base URL (e.g., "https://spectyra.up.railway.app/v1")
|
|
181
|
+
*/
|
|
182
|
+
apiUrl: string;
|
|
183
|
+
/**
|
|
184
|
+
* Your Spectyra API key
|
|
185
|
+
*/
|
|
186
|
+
spectyraKey: string;
|
|
187
|
+
/**
|
|
188
|
+
* LLM provider (e.g., "openai", "anthropic", "gemini", "grok")
|
|
189
|
+
*/
|
|
190
|
+
provider: string;
|
|
191
|
+
/**
|
|
192
|
+
* Your provider API key (BYOK - Bring Your Own Key)
|
|
193
|
+
* This is sent to Spectyra but never stored server-side.
|
|
194
|
+
*/
|
|
195
|
+
providerKey: string;
|
|
196
|
+
}
|
|
197
|
+
export interface ChatOptions {
|
|
198
|
+
/**
|
|
199
|
+
* Model name (e.g., "gpt-4o-mini", "claude-3-5-sonnet")
|
|
200
|
+
*/
|
|
201
|
+
model: string;
|
|
202
|
+
/**
|
|
203
|
+
* Conversation messages
|
|
204
|
+
*/
|
|
205
|
+
messages: ChatMessage[];
|
|
206
|
+
/**
|
|
207
|
+
* Path: "talk" for chat/Q&A, "code" for coding workflows
|
|
208
|
+
*/
|
|
209
|
+
path: Path;
|
|
210
|
+
/**
|
|
211
|
+
* Optimization level (0-4)
|
|
212
|
+
* 0 = Minimal, 1 = Conservative, 2 = Balanced, 3 = Aggressive, 4 = Maximum
|
|
213
|
+
*/
|
|
214
|
+
optimization_level?: number;
|
|
215
|
+
/**
|
|
216
|
+
* Conversation ID for state tracking (optional)
|
|
217
|
+
*/
|
|
218
|
+
conversation_id?: string;
|
|
219
|
+
/**
|
|
220
|
+
* Dry-run mode: estimate savings without making real LLM calls
|
|
221
|
+
*/
|
|
222
|
+
dry_run?: boolean;
|
|
223
|
+
}
|
package/dist/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@spectyra/sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Spectyra SDK for real-time LLM optimization",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"README.md"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"dev": "tsc --watch",
|
|
15
|
+
"prepublishOnly": "npm run build"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"llm",
|
|
19
|
+
"optimization",
|
|
20
|
+
"token-reduction",
|
|
21
|
+
"spectyra",
|
|
22
|
+
"claude",
|
|
23
|
+
"agent",
|
|
24
|
+
"sdk"
|
|
25
|
+
],
|
|
26
|
+
"author": "",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"repository": {
|
|
29
|
+
"type": "git",
|
|
30
|
+
"url": "https://github.com/spectyra/spectyra.git",
|
|
31
|
+
"directory": "packages/sdk"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "^20.0.0",
|
|
36
|
+
"typescript": "~5.4.0"
|
|
37
|
+
}
|
|
38
|
+
}
|