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.
Files changed (99) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +131 -0
  3. package/dist/app.d.ts +1 -0
  4. package/dist/app.js +15 -0
  5. package/dist/cli.d.ts +2 -0
  6. package/dist/cli.js +29 -0
  7. package/dist/commands/clear.d.ts +3 -0
  8. package/dist/commands/clear.js +15 -0
  9. package/dist/commands/help.d.ts +3 -0
  10. package/dist/commands/help.js +29 -0
  11. package/dist/commands/index.d.ts +15 -0
  12. package/dist/commands/index.js +16 -0
  13. package/dist/commands/info.d.ts +3 -0
  14. package/dist/commands/info.js +24 -0
  15. package/dist/commands/mode.d.ts +3 -0
  16. package/dist/commands/mode.js +51 -0
  17. package/dist/commands/model.d.ts +3 -0
  18. package/dist/commands/model.js +14 -0
  19. package/dist/commands/registry.d.ts +32 -0
  20. package/dist/commands/registry.js +86 -0
  21. package/dist/commands/tool-log.d.ts +3 -0
  22. package/dist/commands/tool-log.js +17 -0
  23. package/dist/commands/tool-search.d.ts +3 -0
  24. package/dist/commands/tool-search.js +57 -0
  25. package/dist/commands/tools.d.ts +3 -0
  26. package/dist/commands/tools.js +45 -0
  27. package/dist/commands/types.d.ts +25 -0
  28. package/dist/commands/types.js +4 -0
  29. package/dist/commands/version.d.ts +3 -0
  30. package/dist/commands/version.js +25 -0
  31. package/dist/components/AppInfo.d.ts +1 -0
  32. package/dist/components/AppInfo.js +10 -0
  33. package/dist/components/HomeInput.d.ts +11 -0
  34. package/dist/components/HomeInput.js +328 -0
  35. package/dist/components/Logo.d.ts +1 -0
  36. package/dist/components/Logo.js +15 -0
  37. package/dist/components/Markdown.d.ts +5 -0
  38. package/dist/components/Markdown.js +121 -0
  39. package/dist/components/ProviderBar.d.ts +12 -0
  40. package/dist/components/ProviderBar.js +32 -0
  41. package/dist/components/ShimmerText.d.ts +8 -0
  42. package/dist/components/ShimmerText.js +20 -0
  43. package/dist/components/ToolLogPopup.d.ts +7 -0
  44. package/dist/components/ToolLogPopup.js +87 -0
  45. package/dist/components/common/HistorySelect.d.ts +6 -0
  46. package/dist/components/common/HistorySelect.js +57 -0
  47. package/dist/components/common/Modal.d.ts +10 -0
  48. package/dist/components/common/Modal.js +13 -0
  49. package/dist/components/common/ModeSelect.d.ts +6 -0
  50. package/dist/components/common/ModeSelect.js +13 -0
  51. package/dist/components/common/ModelSelect.d.ts +9 -0
  52. package/dist/components/common/ModelSelect.js +45 -0
  53. package/dist/context/ConversationContext.d.ts +44 -0
  54. package/dist/context/ConversationContext.js +113 -0
  55. package/dist/context/ToolpackContext.d.ts +55 -0
  56. package/dist/context/ToolpackContext.js +221 -0
  57. package/dist/custom-providers/AnthropicCustomAdapter.d.ts +49 -0
  58. package/dist/custom-providers/AnthropicCustomAdapter.js +297 -0
  59. package/dist/custom-providers/XAIAdapter.d.ts +40 -0
  60. package/dist/custom-providers/XAIAdapter.js +295 -0
  61. package/dist/custom-tools/skill-tools/index.d.ts +33 -0
  62. package/dist/custom-tools/skill-tools/index.js +63 -0
  63. package/dist/custom-tools/skill-tools/tools/create/index.d.ts +2 -0
  64. package/dist/custom-tools/skill-tools/tools/create/index.js +93 -0
  65. package/dist/custom-tools/skill-tools/tools/create/schema.d.ts +6 -0
  66. package/dist/custom-tools/skill-tools/tools/create/schema.js +41 -0
  67. package/dist/custom-tools/skill-tools/tools/list/index.d.ts +2 -0
  68. package/dist/custom-tools/skill-tools/tools/list/index.js +113 -0
  69. package/dist/custom-tools/skill-tools/tools/list/schema.d.ts +6 -0
  70. package/dist/custom-tools/skill-tools/tools/list/schema.js +19 -0
  71. package/dist/custom-tools/skill-tools/tools/read/index.d.ts +2 -0
  72. package/dist/custom-tools/skill-tools/tools/read/index.js +124 -0
  73. package/dist/custom-tools/skill-tools/tools/read/schema.d.ts +6 -0
  74. package/dist/custom-tools/skill-tools/tools/read/schema.js +27 -0
  75. package/dist/custom-tools/skill-tools/tools/search/bm25.d.ts +71 -0
  76. package/dist/custom-tools/skill-tools/tools/search/bm25.js +305 -0
  77. package/dist/custom-tools/skill-tools/tools/search/index.d.ts +8 -0
  78. package/dist/custom-tools/skill-tools/tools/search/index.js +63 -0
  79. package/dist/custom-tools/skill-tools/tools/search/schema.d.ts +6 -0
  80. package/dist/custom-tools/skill-tools/tools/search/schema.js +19 -0
  81. package/dist/custom-tools/skill-tools/tools/search/skill-index.d.ts +54 -0
  82. package/dist/custom-tools/skill-tools/tools/search/skill-index.js +251 -0
  83. package/dist/custom-tools/skill-tools/tools/update/index.d.ts +2 -0
  84. package/dist/custom-tools/skill-tools/tools/update/index.js +115 -0
  85. package/dist/custom-tools/skill-tools/tools/update/schema.d.ts +6 -0
  86. package/dist/custom-tools/skill-tools/tools/update/schema.js +41 -0
  87. package/dist/screens/ChatScreen.d.ts +1 -0
  88. package/dist/screens/ChatScreen.js +327 -0
  89. package/dist/screens/HomeScreen.d.ts +1 -0
  90. package/dist/screens/HomeScreen.js +68 -0
  91. package/dist/screens/SettingsScreen.d.ts +1 -0
  92. package/dist/screens/SettingsScreen.js +35 -0
  93. package/dist/services/db.d.ts +31 -0
  94. package/dist/services/db.js +108 -0
  95. package/dist/theme/ThemeContext.d.ts +11 -0
  96. package/dist/theme/ThemeContext.js +31 -0
  97. package/dist/theme/theme.d.ts +17 -0
  98. package/dist/theme/theme.js +82 -0
  99. 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
+ }