toolpack-cli 0.1.0-SNAPSHOT
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/LICENSE +201 -0
- package/README.md +131 -0
- package/dist/app.d.ts +1 -0
- package/dist/app.js +15 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +29 -0
- package/dist/commands/clear.d.ts +3 -0
- package/dist/commands/clear.js +15 -0
- package/dist/commands/help.d.ts +3 -0
- package/dist/commands/help.js +29 -0
- package/dist/commands/index.d.ts +15 -0
- package/dist/commands/index.js +16 -0
- package/dist/commands/info.d.ts +3 -0
- package/dist/commands/info.js +24 -0
- package/dist/commands/mode.d.ts +3 -0
- package/dist/commands/mode.js +51 -0
- package/dist/commands/model.d.ts +3 -0
- package/dist/commands/model.js +14 -0
- package/dist/commands/registry.d.ts +32 -0
- package/dist/commands/registry.js +86 -0
- package/dist/commands/tool-log.d.ts +3 -0
- package/dist/commands/tool-log.js +17 -0
- package/dist/commands/tool-search.d.ts +3 -0
- package/dist/commands/tool-search.js +57 -0
- package/dist/commands/tools.d.ts +3 -0
- package/dist/commands/tools.js +45 -0
- package/dist/commands/types.d.ts +25 -0
- package/dist/commands/types.js +4 -0
- package/dist/commands/version.d.ts +3 -0
- package/dist/commands/version.js +25 -0
- package/dist/components/AppInfo.d.ts +1 -0
- package/dist/components/AppInfo.js +10 -0
- package/dist/components/HomeInput.d.ts +11 -0
- package/dist/components/HomeInput.js +328 -0
- package/dist/components/Logo.d.ts +1 -0
- package/dist/components/Logo.js +15 -0
- package/dist/components/Markdown.d.ts +5 -0
- package/dist/components/Markdown.js +121 -0
- package/dist/components/ProviderBar.d.ts +12 -0
- package/dist/components/ProviderBar.js +32 -0
- package/dist/components/ShimmerText.d.ts +8 -0
- package/dist/components/ShimmerText.js +20 -0
- package/dist/components/ToolLogPopup.d.ts +7 -0
- package/dist/components/ToolLogPopup.js +87 -0
- package/dist/components/common/HistorySelect.d.ts +6 -0
- package/dist/components/common/HistorySelect.js +57 -0
- package/dist/components/common/Modal.d.ts +10 -0
- package/dist/components/common/Modal.js +13 -0
- package/dist/components/common/ModeSelect.d.ts +6 -0
- package/dist/components/common/ModeSelect.js +13 -0
- package/dist/components/common/ModelSelect.d.ts +9 -0
- package/dist/components/common/ModelSelect.js +45 -0
- package/dist/context/ConversationContext.d.ts +44 -0
- package/dist/context/ConversationContext.js +113 -0
- package/dist/context/ToolpackContext.d.ts +55 -0
- package/dist/context/ToolpackContext.js +221 -0
- package/dist/custom-providers/AnthropicCustomAdapter.d.ts +49 -0
- package/dist/custom-providers/AnthropicCustomAdapter.js +297 -0
- package/dist/custom-providers/XAIAdapter.d.ts +40 -0
- package/dist/custom-providers/XAIAdapter.js +295 -0
- package/dist/custom-tools/skill-tools/index.d.ts +33 -0
- package/dist/custom-tools/skill-tools/index.js +63 -0
- package/dist/custom-tools/skill-tools/tools/create/index.d.ts +2 -0
- package/dist/custom-tools/skill-tools/tools/create/index.js +93 -0
- package/dist/custom-tools/skill-tools/tools/create/schema.d.ts +6 -0
- package/dist/custom-tools/skill-tools/tools/create/schema.js +41 -0
- package/dist/custom-tools/skill-tools/tools/list/index.d.ts +2 -0
- package/dist/custom-tools/skill-tools/tools/list/index.js +113 -0
- package/dist/custom-tools/skill-tools/tools/list/schema.d.ts +6 -0
- package/dist/custom-tools/skill-tools/tools/list/schema.js +19 -0
- package/dist/custom-tools/skill-tools/tools/read/index.d.ts +2 -0
- package/dist/custom-tools/skill-tools/tools/read/index.js +124 -0
- package/dist/custom-tools/skill-tools/tools/read/schema.d.ts +6 -0
- package/dist/custom-tools/skill-tools/tools/read/schema.js +27 -0
- package/dist/custom-tools/skill-tools/tools/search/bm25.d.ts +71 -0
- package/dist/custom-tools/skill-tools/tools/search/bm25.js +305 -0
- package/dist/custom-tools/skill-tools/tools/search/index.d.ts +8 -0
- package/dist/custom-tools/skill-tools/tools/search/index.js +63 -0
- package/dist/custom-tools/skill-tools/tools/search/schema.d.ts +6 -0
- package/dist/custom-tools/skill-tools/tools/search/schema.js +19 -0
- package/dist/custom-tools/skill-tools/tools/search/skill-index.d.ts +54 -0
- package/dist/custom-tools/skill-tools/tools/search/skill-index.js +251 -0
- package/dist/custom-tools/skill-tools/tools/update/index.d.ts +2 -0
- package/dist/custom-tools/skill-tools/tools/update/index.js +115 -0
- package/dist/custom-tools/skill-tools/tools/update/schema.d.ts +6 -0
- package/dist/custom-tools/skill-tools/tools/update/schema.js +41 -0
- package/dist/screens/ChatScreen.d.ts +1 -0
- package/dist/screens/ChatScreen.js +327 -0
- package/dist/screens/HomeScreen.d.ts +1 -0
- package/dist/screens/HomeScreen.js +68 -0
- package/dist/screens/SettingsScreen.d.ts +1 -0
- package/dist/screens/SettingsScreen.js +35 -0
- package/dist/services/db.d.ts +31 -0
- package/dist/services/db.js +108 -0
- package/dist/theme/ThemeContext.d.ts +11 -0
- package/dist/theme/ThemeContext.js +31 -0
- package/dist/theme/theme.d.ts +17 -0
- package/dist/theme/theme.js +82 -0
- package/package.json +101 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { Toolpack, ModeConfig, ToolLogEvent, WorkflowProgress } from 'toolpack-sdk';
|
|
3
|
+
/**
|
|
4
|
+
* Clean React Context wrapper for the Toolpack SDK.
|
|
5
|
+
* Manages SDK-related state only: modes, models, and the toolpack instance.
|
|
6
|
+
* Application state (conversations, history) is handled by ConversationContext.
|
|
7
|
+
*/
|
|
8
|
+
export interface ModelCategory {
|
|
9
|
+
provider: string;
|
|
10
|
+
displayName: string;
|
|
11
|
+
models: {
|
|
12
|
+
label: string;
|
|
13
|
+
value: string;
|
|
14
|
+
capabilities: any;
|
|
15
|
+
}[];
|
|
16
|
+
}
|
|
17
|
+
interface ToolpackContextType {
|
|
18
|
+
toolpack: Toolpack | null;
|
|
19
|
+
activeMode: ModeConfig | null;
|
|
20
|
+
modes: ModeConfig[];
|
|
21
|
+
activeModel: string;
|
|
22
|
+
activeModelCapabilities: {
|
|
23
|
+
toolCalling?: boolean;
|
|
24
|
+
vision?: boolean;
|
|
25
|
+
embeddings?: boolean;
|
|
26
|
+
} | null;
|
|
27
|
+
models: {
|
|
28
|
+
label: string;
|
|
29
|
+
value: string;
|
|
30
|
+
provider?: string;
|
|
31
|
+
}[];
|
|
32
|
+
modelCategories: ModelCategory[];
|
|
33
|
+
loadingModels: boolean;
|
|
34
|
+
setMode: (modeName: string) => void;
|
|
35
|
+
setModel: (model: {
|
|
36
|
+
value: string;
|
|
37
|
+
provider?: string;
|
|
38
|
+
capabilities?: any;
|
|
39
|
+
}) => void;
|
|
40
|
+
cycleMode: () => void;
|
|
41
|
+
workflowProgress: WorkflowProgress | null;
|
|
42
|
+
clearWorkflowProgress: () => void;
|
|
43
|
+
toolHistory: ToolLogEvent[];
|
|
44
|
+
clearToolHistory: () => void;
|
|
45
|
+
toolsUnsupportedWarning: string | null;
|
|
46
|
+
clearToolsUnsupportedWarning: () => void;
|
|
47
|
+
setToolsUnsupportedWarning: (msg: string) => void;
|
|
48
|
+
configSource: 'local' | 'global' | 'base' | 'default';
|
|
49
|
+
activeConfigPath: string | null;
|
|
50
|
+
}
|
|
51
|
+
export declare function ToolpackProvider({ children }: {
|
|
52
|
+
children: ReactNode;
|
|
53
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
54
|
+
export declare function useToolpack(): ToolpackContextType;
|
|
55
|
+
export {};
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { createContext, useContext, useEffect, useState, useCallback, } from 'react';
|
|
3
|
+
import { Toolpack, createMode, loadRuntimeConfig, getRuntimeConfigStatus, } from 'toolpack-sdk';
|
|
4
|
+
import { XAIAdapter } from '../custom-providers/XAIAdapter.js';
|
|
5
|
+
import { skillToolsProject } from '../custom-tools/skill-tools/index.js';
|
|
6
|
+
import * as fs from 'fs';
|
|
7
|
+
import * as os from 'os';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
const ToolpackContext = createContext(undefined);
|
|
10
|
+
export function ToolpackProvider({ children }) {
|
|
11
|
+
const [toolpack, setToolpack] = useState(null);
|
|
12
|
+
const [activeMode, setActiveMode] = useState(null);
|
|
13
|
+
const [modes, setModes] = useState([]);
|
|
14
|
+
const [activeModel, setActiveModel] = useState('gpt-4.1');
|
|
15
|
+
const [workflowProgress, setWorkflowProgress] = useState(null);
|
|
16
|
+
const [toolHistory, setToolHistory] = useState([]);
|
|
17
|
+
const [models, setModels] = useState([]);
|
|
18
|
+
const [modelCategories, setModelCategories] = useState([]);
|
|
19
|
+
const [loadingModels, setLoadingModels] = useState(true);
|
|
20
|
+
const [activeModelCapabilities, setActiveModelCapabilities] = useState(null);
|
|
21
|
+
const [toolsUnsupportedWarning, setToolsUnsupportedWarning] = useState(null);
|
|
22
|
+
const [configSource, setConfigSource] = useState('default');
|
|
23
|
+
const [activeConfigPath, setActiveConfigPath] = useState(null);
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
async function init() {
|
|
26
|
+
// Get config status
|
|
27
|
+
const configStatus = getRuntimeConfigStatus();
|
|
28
|
+
setConfigSource(configStatus.configSource);
|
|
29
|
+
setActiveConfigPath(configStatus.activeConfigPath);
|
|
30
|
+
// Load merged runtime configuration
|
|
31
|
+
const runtimeConfig = loadRuntimeConfig();
|
|
32
|
+
// To pass the merged config to Toolpack.init (which expects a file),
|
|
33
|
+
// we create a temporary file with the merged contents.
|
|
34
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'toolpack-cli-'));
|
|
35
|
+
const tempConfigPath = path.join(tempDir, 'toolpack.config.json');
|
|
36
|
+
fs.writeFileSync(tempConfigPath, JSON.stringify(runtimeConfig, null, 4));
|
|
37
|
+
// Define a sample custom mode for demonstration
|
|
38
|
+
const simpleChat = createMode({
|
|
39
|
+
name: 'simpleChat',
|
|
40
|
+
displayName: 'Simple Chat',
|
|
41
|
+
systemPrompt: 'You are a helpful assistant. Provide clear and concise responses.',
|
|
42
|
+
blockAllTools: true,
|
|
43
|
+
});
|
|
44
|
+
const instance = await Toolpack.init({
|
|
45
|
+
defaultMode: 'agent',
|
|
46
|
+
configPath: tempConfigPath, // Use our merged temp config
|
|
47
|
+
providers: {
|
|
48
|
+
openai: {},
|
|
49
|
+
anthropic: {},
|
|
50
|
+
gemini: {},
|
|
51
|
+
ollama: {},
|
|
52
|
+
},
|
|
53
|
+
defaultProvider: 'openai',
|
|
54
|
+
tools: true,
|
|
55
|
+
customModes: [simpleChat],
|
|
56
|
+
customProviders: [new XAIAdapter()],
|
|
57
|
+
customTools: [skillToolsProject],
|
|
58
|
+
modeOverrides: {
|
|
59
|
+
agent: {
|
|
60
|
+
// Override the agent mode system prompt to include skill search and read
|
|
61
|
+
systemPrompt: [
|
|
62
|
+
'You are an autonomous AI agent with full access to all available tools.',
|
|
63
|
+
'You must use the tools provided to accomplish tasks end-to-end proactively.',
|
|
64
|
+
'If you require a capability that is not listed in your current tools, ALWAYS use `tool.search` to find it before improvising or giving up.',
|
|
65
|
+
'Before considering a tool to call, make sure that is the right tool for the job as per the users prompt.',
|
|
66
|
+
'You also have access to `skill.search` and `skill.read` tools to find reusable instructions if needed.',
|
|
67
|
+
'Verify your actions and check for success or failure states.',
|
|
68
|
+
'Explain your actions briefly as you go.',
|
|
69
|
+
].join('\n'),
|
|
70
|
+
// Override tool search configuration
|
|
71
|
+
toolSearch: {
|
|
72
|
+
alwaysLoadedTools: [
|
|
73
|
+
// Empty array - AI must search for all tools
|
|
74
|
+
// Or specify specific tools you always want available:
|
|
75
|
+
// 'tool.search',
|
|
76
|
+
// 'fs.read_file',
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
// Cleanup temp file
|
|
83
|
+
try {
|
|
84
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
85
|
+
}
|
|
86
|
+
catch (e) {
|
|
87
|
+
// Ignore cleanup errors
|
|
88
|
+
}
|
|
89
|
+
// Set up tool log event listener for history
|
|
90
|
+
const client = instance.getClient();
|
|
91
|
+
if (client) {
|
|
92
|
+
client.on('tool:log', (event) => {
|
|
93
|
+
setToolHistory(prev => [...prev, event]);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
// Set up workflow progress event listener
|
|
97
|
+
const executor = instance.getWorkflowExecutor();
|
|
98
|
+
if (executor) {
|
|
99
|
+
executor.on('workflow:progress', (progress) => {
|
|
100
|
+
setWorkflowProgress(progress);
|
|
101
|
+
});
|
|
102
|
+
executor.on('workflow:completed', () => {
|
|
103
|
+
// Don't null out — let the 'completed' progress state persist
|
|
104
|
+
// so the UI can show "Workflow completed." text.
|
|
105
|
+
// It gets cleared when the next request starts.
|
|
106
|
+
});
|
|
107
|
+
executor.on('workflow:failed', () => {
|
|
108
|
+
// Don't null out — let the 'failed' progress state persist.
|
|
109
|
+
// It gets cleared when the next request starts.
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
setToolpack(instance);
|
|
113
|
+
setActiveMode(instance.getMode() || null);
|
|
114
|
+
setModes(instance.getModes() || []);
|
|
115
|
+
// Fetch dynamic models list
|
|
116
|
+
setLoadingModels(true);
|
|
117
|
+
try {
|
|
118
|
+
const providersInfo = await instance.listProviders();
|
|
119
|
+
const categories = providersInfo
|
|
120
|
+
.map(p => ({
|
|
121
|
+
provider: p.name,
|
|
122
|
+
displayName: p.displayName || p.name,
|
|
123
|
+
models: p.models.map((m) => ({
|
|
124
|
+
label: m.displayName,
|
|
125
|
+
value: m.id,
|
|
126
|
+
capabilities: m.capabilities,
|
|
127
|
+
})),
|
|
128
|
+
}))
|
|
129
|
+
.filter(c => c.models.length > 0);
|
|
130
|
+
setModelCategories(categories);
|
|
131
|
+
// Also set flat list for backward compatibility of setModel
|
|
132
|
+
const flatList = categories.flatMap(c => c.models.map((m) => ({
|
|
133
|
+
label: m.label,
|
|
134
|
+
value: m.value,
|
|
135
|
+
provider: c.provider,
|
|
136
|
+
})));
|
|
137
|
+
setModels(flatList);
|
|
138
|
+
}
|
|
139
|
+
catch (err) {
|
|
140
|
+
console.error('Failed to list providers:', err);
|
|
141
|
+
}
|
|
142
|
+
finally {
|
|
143
|
+
setLoadingModels(false);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
init();
|
|
147
|
+
}, []);
|
|
148
|
+
const setMode = (modeName) => {
|
|
149
|
+
if (toolpack) {
|
|
150
|
+
toolpack.setMode(modeName);
|
|
151
|
+
setActiveMode(toolpack.getMode() || null);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
const cycleMode = () => {
|
|
155
|
+
if (toolpack) {
|
|
156
|
+
toolpack.cycleMode();
|
|
157
|
+
setActiveMode(toolpack.getMode() || null);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
const setModel = ({ value, provider, capabilities, }) => {
|
|
161
|
+
setActiveModel(value);
|
|
162
|
+
setActiveModelCapabilities(capabilities || null);
|
|
163
|
+
setToolsUnsupportedWarning(null); // Clear any previous warning
|
|
164
|
+
// If this model belongs to a specific provider, switch the active provider
|
|
165
|
+
if (toolpack && provider) {
|
|
166
|
+
try {
|
|
167
|
+
toolpack.setProvider(provider);
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
// Provider not registered (e.g. no API key) — ignore silently
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
else if (toolpack && !provider) {
|
|
174
|
+
// Default back to openai for non-provider-specific models
|
|
175
|
+
try {
|
|
176
|
+
toolpack.setProvider('openai');
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
/* ignore */
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
const clearToolHistory = useCallback(() => {
|
|
184
|
+
setToolHistory([]);
|
|
185
|
+
}, []);
|
|
186
|
+
const clearToolsUnsupportedWarning = useCallback(() => {
|
|
187
|
+
setToolsUnsupportedWarning(null);
|
|
188
|
+
}, []);
|
|
189
|
+
const clearWorkflowProgress = useCallback(() => {
|
|
190
|
+
setWorkflowProgress(null);
|
|
191
|
+
}, []);
|
|
192
|
+
return (_jsx(ToolpackContext.Provider, { value: {
|
|
193
|
+
toolpack,
|
|
194
|
+
activeMode,
|
|
195
|
+
modes,
|
|
196
|
+
activeModel,
|
|
197
|
+
activeModelCapabilities,
|
|
198
|
+
models,
|
|
199
|
+
modelCategories,
|
|
200
|
+
loadingModels,
|
|
201
|
+
setMode,
|
|
202
|
+
setModel,
|
|
203
|
+
cycleMode,
|
|
204
|
+
workflowProgress,
|
|
205
|
+
toolHistory,
|
|
206
|
+
clearToolHistory,
|
|
207
|
+
clearWorkflowProgress,
|
|
208
|
+
toolsUnsupportedWarning,
|
|
209
|
+
clearToolsUnsupportedWarning,
|
|
210
|
+
setToolsUnsupportedWarning,
|
|
211
|
+
configSource,
|
|
212
|
+
activeConfigPath,
|
|
213
|
+
}, children: children }));
|
|
214
|
+
}
|
|
215
|
+
export function useToolpack() {
|
|
216
|
+
const context = useContext(ToolpackContext);
|
|
217
|
+
if (!context) {
|
|
218
|
+
throw new Error('useToolpack must be used within a ToolpackProvider');
|
|
219
|
+
}
|
|
220
|
+
return context;
|
|
221
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anthropic Custom Adapter - Custom Provider Example
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates how to create a custom provider adapter for the Toolpack SDK.
|
|
5
|
+
* This is a custom implementation of Anthropic's API for testing purposes.
|
|
6
|
+
*
|
|
7
|
+
* API Docs: https://docs.anthropic.com/en/api
|
|
8
|
+
*/
|
|
9
|
+
import { ProviderAdapter, CompletionRequest, CompletionResponse, CompletionChunk, EmbeddingRequest, EmbeddingResponse, ProviderModelInfo } from 'toolpack-sdk';
|
|
10
|
+
export interface AnthropicCustomAdapterConfig {
|
|
11
|
+
/** API key. If not provided, reads from ANTHROPIC_API_KEY or TOOLPACK_ANTHROPIC_KEY env vars */
|
|
12
|
+
apiKey?: string;
|
|
13
|
+
baseUrl?: string;
|
|
14
|
+
defaultModel?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare class AnthropicCustomAdapter implements ProviderAdapter {
|
|
17
|
+
name: string;
|
|
18
|
+
private apiKey;
|
|
19
|
+
private baseUrl;
|
|
20
|
+
private defaultModel;
|
|
21
|
+
constructor(config?: AnthropicCustomAdapterConfig);
|
|
22
|
+
getDisplayName(): string;
|
|
23
|
+
getModels(): Promise<ProviderModelInfo[]>;
|
|
24
|
+
private ensureApiKey;
|
|
25
|
+
/**
|
|
26
|
+
* Sanitize tool name for Anthropic API (must match ^[a-zA-Z0-9_-]{1,128}$)
|
|
27
|
+
* Converts dots to underscores: fs.read_file -> fs_read_file
|
|
28
|
+
*/
|
|
29
|
+
private sanitizeToolName;
|
|
30
|
+
/**
|
|
31
|
+
* Restore original tool name from sanitized version
|
|
32
|
+
*/
|
|
33
|
+
private restoreToolName;
|
|
34
|
+
generate(request: CompletionRequest): Promise<CompletionResponse>;
|
|
35
|
+
stream(request: CompletionRequest): AsyncGenerator<CompletionChunk>;
|
|
36
|
+
embed(_request: EmbeddingRequest): Promise<EmbeddingResponse>;
|
|
37
|
+
supportsFileUpload(): boolean;
|
|
38
|
+
uploadFile(_request: {
|
|
39
|
+
filePath?: string;
|
|
40
|
+
data?: string | Buffer;
|
|
41
|
+
filename?: string;
|
|
42
|
+
purpose?: string;
|
|
43
|
+
}): Promise<{
|
|
44
|
+
id: string;
|
|
45
|
+
url?: string;
|
|
46
|
+
expiresAt?: Date;
|
|
47
|
+
}>;
|
|
48
|
+
deleteFile(_fileId: string): Promise<void>;
|
|
49
|
+
}
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anthropic Custom Adapter - Custom Provider Example
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates how to create a custom provider adapter for the Toolpack SDK.
|
|
5
|
+
* This is a custom implementation of Anthropic's API for testing purposes.
|
|
6
|
+
*
|
|
7
|
+
* API Docs: https://docs.anthropic.com/en/api
|
|
8
|
+
*/
|
|
9
|
+
export class AnthropicCustomAdapter {
|
|
10
|
+
constructor(config = {}) {
|
|
11
|
+
Object.defineProperty(this, "name", {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
configurable: true,
|
|
14
|
+
writable: true,
|
|
15
|
+
value: 'anthropic-custom'
|
|
16
|
+
});
|
|
17
|
+
Object.defineProperty(this, "apiKey", {
|
|
18
|
+
enumerable: true,
|
|
19
|
+
configurable: true,
|
|
20
|
+
writable: true,
|
|
21
|
+
value: void 0
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(this, "baseUrl", {
|
|
24
|
+
enumerable: true,
|
|
25
|
+
configurable: true,
|
|
26
|
+
writable: true,
|
|
27
|
+
value: void 0
|
|
28
|
+
});
|
|
29
|
+
Object.defineProperty(this, "defaultModel", {
|
|
30
|
+
enumerable: true,
|
|
31
|
+
configurable: true,
|
|
32
|
+
writable: true,
|
|
33
|
+
value: void 0
|
|
34
|
+
});
|
|
35
|
+
// Resolve API key from config or environment (defer error to usage time)
|
|
36
|
+
this.apiKey =
|
|
37
|
+
config.apiKey ||
|
|
38
|
+
process.env['ANTHROPIC_API_KEY'] ||
|
|
39
|
+
process.env['TOOLPACK_ANTHROPIC_KEY'] ||
|
|
40
|
+
null;
|
|
41
|
+
this.baseUrl = config.baseUrl || 'https://api.anthropic.com/v1';
|
|
42
|
+
this.defaultModel = config.defaultModel || 'claude-sonnet-4-20250514';
|
|
43
|
+
}
|
|
44
|
+
getDisplayName() {
|
|
45
|
+
return 'Anthropic (Custom)';
|
|
46
|
+
}
|
|
47
|
+
async getModels() {
|
|
48
|
+
return [
|
|
49
|
+
{
|
|
50
|
+
id: 'claude-3-7-sonnet-20250219',
|
|
51
|
+
displayName: 'Claude 3.7 Sonnet',
|
|
52
|
+
capabilities: {
|
|
53
|
+
chat: true,
|
|
54
|
+
streaming: true,
|
|
55
|
+
toolCalling: true,
|
|
56
|
+
embeddings: false,
|
|
57
|
+
vision: true,
|
|
58
|
+
},
|
|
59
|
+
contextWindow: 200000,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
id: 'claude-3-5-sonnet-20241022',
|
|
63
|
+
displayName: 'Claude 3.5 Sonnet',
|
|
64
|
+
capabilities: {
|
|
65
|
+
chat: true,
|
|
66
|
+
streaming: true,
|
|
67
|
+
toolCalling: true,
|
|
68
|
+
embeddings: false,
|
|
69
|
+
vision: true,
|
|
70
|
+
},
|
|
71
|
+
contextWindow: 200000,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
id: 'claude-3-5-haiku-20241022',
|
|
75
|
+
displayName: 'Claude 3.5 Haiku',
|
|
76
|
+
capabilities: {
|
|
77
|
+
chat: true,
|
|
78
|
+
streaming: true,
|
|
79
|
+
toolCalling: true,
|
|
80
|
+
embeddings: false,
|
|
81
|
+
vision: false,
|
|
82
|
+
},
|
|
83
|
+
contextWindow: 200000,
|
|
84
|
+
},
|
|
85
|
+
];
|
|
86
|
+
}
|
|
87
|
+
ensureApiKey() {
|
|
88
|
+
if (!this.apiKey) {
|
|
89
|
+
throw new Error('No API key found for Anthropic Custom. Set ANTHROPIC_API_KEY or TOOLPACK_ANTHROPIC_KEY environment variable, ' +
|
|
90
|
+
'or pass apiKey in config.');
|
|
91
|
+
}
|
|
92
|
+
return this.apiKey;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Sanitize tool name for Anthropic API (must match ^[a-zA-Z0-9_-]{1,128}$)
|
|
96
|
+
* Converts dots to underscores: fs.read_file -> fs_read_file
|
|
97
|
+
*/
|
|
98
|
+
sanitizeToolName(name) {
|
|
99
|
+
return name.replace(/\./g, '_');
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Restore original tool name from sanitized version
|
|
103
|
+
*/
|
|
104
|
+
restoreToolName(sanitized, originalTools) {
|
|
105
|
+
// Find the original tool name that matches when sanitized
|
|
106
|
+
const original = originalTools?.find(t => this.sanitizeToolName(t.function.name) === sanitized);
|
|
107
|
+
return original?.function.name || sanitized.replace(/_/g, '.');
|
|
108
|
+
}
|
|
109
|
+
async generate(request) {
|
|
110
|
+
const apiKey = this.ensureApiKey();
|
|
111
|
+
const model = request.model || this.defaultModel;
|
|
112
|
+
// Convert messages to Anthropic format
|
|
113
|
+
const systemMessage = request.messages.find(m => m.role === 'system')?.content || '';
|
|
114
|
+
const messages = request.messages
|
|
115
|
+
.filter(m => m.role !== 'system')
|
|
116
|
+
.map(m => ({
|
|
117
|
+
role: m.role === 'assistant' ? 'assistant' : 'user',
|
|
118
|
+
content: typeof m.content === 'string' ? m.content : JSON.stringify(m.content),
|
|
119
|
+
}));
|
|
120
|
+
const response = await fetch(`${this.baseUrl}/messages`, {
|
|
121
|
+
method: 'POST',
|
|
122
|
+
headers: {
|
|
123
|
+
'x-api-key': apiKey,
|
|
124
|
+
'anthropic-version': '2023-06-01',
|
|
125
|
+
'Content-Type': 'application/json',
|
|
126
|
+
},
|
|
127
|
+
body: JSON.stringify({
|
|
128
|
+
model,
|
|
129
|
+
messages,
|
|
130
|
+
system: systemMessage || undefined,
|
|
131
|
+
max_tokens: request.max_tokens || 4096,
|
|
132
|
+
temperature: request.temperature,
|
|
133
|
+
top_p: request.top_p,
|
|
134
|
+
tools: request.tools?.map(t => ({
|
|
135
|
+
name: this.sanitizeToolName(t.function.name),
|
|
136
|
+
description: t.function.description,
|
|
137
|
+
input_schema: t.function.parameters,
|
|
138
|
+
})),
|
|
139
|
+
}),
|
|
140
|
+
});
|
|
141
|
+
if (!response.ok) {
|
|
142
|
+
const error = await response.text();
|
|
143
|
+
throw new Error(`Anthropic Custom API error: ${response.status} - ${error}`);
|
|
144
|
+
}
|
|
145
|
+
const data = (await response.json());
|
|
146
|
+
// Extract tool calls if present (restore original tool names)
|
|
147
|
+
let toolCalls = undefined;
|
|
148
|
+
const toolUseBlocks = data.content?.filter((block) => block.type === 'tool_use');
|
|
149
|
+
if (toolUseBlocks && toolUseBlocks.length > 0) {
|
|
150
|
+
toolCalls = toolUseBlocks.map((block) => ({
|
|
151
|
+
id: block.id,
|
|
152
|
+
name: this.restoreToolName(block.name, request.tools),
|
|
153
|
+
arguments: block.input,
|
|
154
|
+
}));
|
|
155
|
+
}
|
|
156
|
+
// Extract text content
|
|
157
|
+
const textBlocks = data.content?.filter((block) => block.type === 'text');
|
|
158
|
+
const content = textBlocks?.map((block) => block.text).join('') || null;
|
|
159
|
+
return {
|
|
160
|
+
content,
|
|
161
|
+
usage: data.usage
|
|
162
|
+
? {
|
|
163
|
+
prompt_tokens: data.usage.input_tokens,
|
|
164
|
+
completion_tokens: data.usage.output_tokens,
|
|
165
|
+
total_tokens: data.usage.input_tokens + data.usage.output_tokens,
|
|
166
|
+
}
|
|
167
|
+
: undefined,
|
|
168
|
+
finish_reason: data.stop_reason === 'tool_use'
|
|
169
|
+
? 'tool_calls'
|
|
170
|
+
: data.stop_reason === 'max_tokens'
|
|
171
|
+
? 'length'
|
|
172
|
+
: 'stop',
|
|
173
|
+
tool_calls: toolCalls,
|
|
174
|
+
raw: data,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
async *stream(request) {
|
|
178
|
+
const apiKey = this.ensureApiKey();
|
|
179
|
+
const model = request.model || this.defaultModel;
|
|
180
|
+
// Convert messages to Anthropic format
|
|
181
|
+
const systemMessage = request.messages.find(m => m.role === 'system')?.content || '';
|
|
182
|
+
const messages = request.messages
|
|
183
|
+
.filter(m => m.role !== 'system')
|
|
184
|
+
.map(m => ({
|
|
185
|
+
role: m.role === 'assistant' ? 'assistant' : 'user',
|
|
186
|
+
content: typeof m.content === 'string' ? m.content : JSON.stringify(m.content),
|
|
187
|
+
}));
|
|
188
|
+
const response = await fetch(`${this.baseUrl}/messages`, {
|
|
189
|
+
method: 'POST',
|
|
190
|
+
headers: {
|
|
191
|
+
'x-api-key': apiKey,
|
|
192
|
+
'anthropic-version': '2023-06-01',
|
|
193
|
+
'Content-Type': 'application/json',
|
|
194
|
+
},
|
|
195
|
+
body: JSON.stringify({
|
|
196
|
+
model,
|
|
197
|
+
messages,
|
|
198
|
+
system: systemMessage || undefined,
|
|
199
|
+
max_tokens: request.max_tokens || 4096,
|
|
200
|
+
temperature: request.temperature,
|
|
201
|
+
stream: true,
|
|
202
|
+
tools: request.tools?.map(t => ({
|
|
203
|
+
name: this.sanitizeToolName(t.function.name),
|
|
204
|
+
description: t.function.description,
|
|
205
|
+
input_schema: t.function.parameters,
|
|
206
|
+
})),
|
|
207
|
+
}),
|
|
208
|
+
});
|
|
209
|
+
if (!response.ok) {
|
|
210
|
+
const error = await response.text();
|
|
211
|
+
throw new Error(`Anthropic Custom API error: ${response.status} - ${error}`);
|
|
212
|
+
}
|
|
213
|
+
const reader = response.body.getReader();
|
|
214
|
+
const decoder = new TextDecoder();
|
|
215
|
+
let buffer = '';
|
|
216
|
+
// Accumulate tool use blocks (store sanitized names, restore later)
|
|
217
|
+
const toolUseBlocks = new Map();
|
|
218
|
+
while (true) {
|
|
219
|
+
const { done, value } = await reader.read();
|
|
220
|
+
if (done)
|
|
221
|
+
break;
|
|
222
|
+
buffer += decoder.decode(value, { stream: true });
|
|
223
|
+
const lines = buffer.split('\n');
|
|
224
|
+
buffer = lines.pop() || '';
|
|
225
|
+
for (const line of lines) {
|
|
226
|
+
if (!line.startsWith('data: '))
|
|
227
|
+
continue;
|
|
228
|
+
const data = line.slice(6).trim();
|
|
229
|
+
try {
|
|
230
|
+
const event = JSON.parse(data);
|
|
231
|
+
// Handle content block delta (text streaming)
|
|
232
|
+
if (event.type === 'content_block_delta' &&
|
|
233
|
+
event.delta?.type === 'text_delta') {
|
|
234
|
+
yield { delta: event.delta.text };
|
|
235
|
+
}
|
|
236
|
+
// Handle tool use blocks
|
|
237
|
+
if (event.type === 'content_block_start' &&
|
|
238
|
+
event.content_block?.type === 'tool_use') {
|
|
239
|
+
const idx = event.index ?? 0;
|
|
240
|
+
toolUseBlocks.set(idx, {
|
|
241
|
+
id: event.content_block.id,
|
|
242
|
+
name: event.content_block.name,
|
|
243
|
+
input: '',
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
if (event.type === 'content_block_delta' &&
|
|
247
|
+
event.delta?.type === 'input_json_delta') {
|
|
248
|
+
const idx = event.index ?? 0;
|
|
249
|
+
const block = toolUseBlocks.get(idx);
|
|
250
|
+
if (block) {
|
|
251
|
+
block.input += event.delta.partial_json;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// Handle message completion
|
|
255
|
+
if (event.type === 'message_delta') {
|
|
256
|
+
const stopReason = event.delta?.stop_reason;
|
|
257
|
+
if (stopReason === 'end_turn') {
|
|
258
|
+
yield { delta: '', finish_reason: 'stop' };
|
|
259
|
+
}
|
|
260
|
+
else if (stopReason === 'max_tokens') {
|
|
261
|
+
yield { delta: '', finish_reason: 'length' };
|
|
262
|
+
}
|
|
263
|
+
else if (stopReason === 'tool_use') {
|
|
264
|
+
const toolCalls = Array.from(toolUseBlocks.values()).map(block => ({
|
|
265
|
+
id: block.id,
|
|
266
|
+
name: this.restoreToolName(block.name, request.tools),
|
|
267
|
+
arguments: JSON.parse(block.input || '{}'),
|
|
268
|
+
}));
|
|
269
|
+
yield {
|
|
270
|
+
delta: '',
|
|
271
|
+
finish_reason: 'tool_calls',
|
|
272
|
+
tool_calls: toolCalls,
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
catch {
|
|
278
|
+
// Ignore parse errors from partial lines
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
async embed(_request) {
|
|
284
|
+
// Anthropic doesn't support embeddings - throw a clear error
|
|
285
|
+
throw new Error('Anthropic does not support embeddings. Use OpenAI or another provider for embeddings.');
|
|
286
|
+
}
|
|
287
|
+
// File upload is not supported by Anthropic
|
|
288
|
+
supportsFileUpload() {
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
291
|
+
async uploadFile(_request) {
|
|
292
|
+
throw new Error('Anthropic does not support file uploads.');
|
|
293
|
+
}
|
|
294
|
+
async deleteFile(_fileId) {
|
|
295
|
+
throw new Error('Anthropic does not support file operations.');
|
|
296
|
+
}
|
|
297
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* xAI Adapter - Custom Provider Example
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates how to create a custom provider adapter for the Toolpack SDK.
|
|
5
|
+
* xAI provides Grok models for fast, intelligent AI responses.
|
|
6
|
+
*
|
|
7
|
+
* API Docs: https://docs.x.ai/api
|
|
8
|
+
*/
|
|
9
|
+
import { ProviderAdapter, CompletionRequest, CompletionResponse, CompletionChunk, EmbeddingRequest, EmbeddingResponse, ProviderModelInfo } from 'toolpack-sdk';
|
|
10
|
+
export interface XAIAdapterConfig {
|
|
11
|
+
/** API key. If not provided, reads from XAI_API_KEY or TOOLPACK_XAI_KEY env vars */
|
|
12
|
+
apiKey?: string;
|
|
13
|
+
baseUrl?: string;
|
|
14
|
+
defaultModel?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare class XAIAdapter implements ProviderAdapter {
|
|
17
|
+
name: string;
|
|
18
|
+
private apiKey;
|
|
19
|
+
private baseUrl;
|
|
20
|
+
private defaultModel;
|
|
21
|
+
constructor(config?: XAIAdapterConfig);
|
|
22
|
+
getDisplayName(): string;
|
|
23
|
+
getModels(): Promise<ProviderModelInfo[]>;
|
|
24
|
+
private ensureApiKey;
|
|
25
|
+
generate(request: CompletionRequest): Promise<CompletionResponse>;
|
|
26
|
+
stream(request: CompletionRequest): AsyncGenerator<CompletionChunk>;
|
|
27
|
+
embed(_request: EmbeddingRequest): Promise<EmbeddingResponse>;
|
|
28
|
+
supportsFileUpload(): boolean;
|
|
29
|
+
uploadFile(_request: {
|
|
30
|
+
filePath?: string;
|
|
31
|
+
data?: string | Buffer;
|
|
32
|
+
filename?: string;
|
|
33
|
+
purpose?: string;
|
|
34
|
+
}): Promise<{
|
|
35
|
+
id: string;
|
|
36
|
+
url?: string;
|
|
37
|
+
expiresAt?: Date;
|
|
38
|
+
}>;
|
|
39
|
+
deleteFile(_fileId: string): Promise<void>;
|
|
40
|
+
}
|