@boostecom/provider 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -0
- package/dist/index.cjs +2522 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +848 -0
- package/dist/index.d.ts +848 -0
- package/dist/index.js +2484 -0
- package/dist/index.js.map +1 -0
- package/docs/content/README.md +337 -0
- package/docs/content/agent-teams.mdx +324 -0
- package/docs/content/api.mdx +757 -0
- package/docs/content/best-practices.mdx +624 -0
- package/docs/content/examples.mdx +675 -0
- package/docs/content/guide.mdx +516 -0
- package/docs/content/index.mdx +99 -0
- package/docs/content/installation.mdx +246 -0
- package/docs/content/skills.mdx +548 -0
- package/docs/content/troubleshooting.mdx +588 -0
- package/docs/examples/README.md +499 -0
- package/docs/examples/abort-signal.ts +125 -0
- package/docs/examples/agent-teams.ts +122 -0
- package/docs/examples/basic-usage.ts +73 -0
- package/docs/examples/check-cli.ts +51 -0
- package/docs/examples/conversation-history.ts +69 -0
- package/docs/examples/custom-config.ts +90 -0
- package/docs/examples/generate-object-constraints.ts +209 -0
- package/docs/examples/generate-object.ts +211 -0
- package/docs/examples/hooks-callbacks.ts +63 -0
- package/docs/examples/images.ts +76 -0
- package/docs/examples/integration-test.ts +241 -0
- package/docs/examples/limitations.ts +150 -0
- package/docs/examples/logging-custom-logger.ts +99 -0
- package/docs/examples/logging-default.ts +55 -0
- package/docs/examples/logging-disabled.ts +74 -0
- package/docs/examples/logging-verbose.ts +64 -0
- package/docs/examples/long-running-tasks.ts +179 -0
- package/docs/examples/message-injection.ts +210 -0
- package/docs/examples/mid-stream-injection.ts +126 -0
- package/docs/examples/run-all-examples.sh +48 -0
- package/docs/examples/sdk-tools-callbacks.ts +49 -0
- package/docs/examples/skills-discovery.ts +144 -0
- package/docs/examples/skills-management.ts +140 -0
- package/docs/examples/stream-object.ts +80 -0
- package/docs/examples/streaming.ts +52 -0
- package/docs/examples/structured-output-repro.ts +227 -0
- package/docs/examples/tool-management.ts +215 -0
- package/docs/examples/tool-streaming.ts +132 -0
- package/docs/examples/zod4-compatibility-test.ts +290 -0
- package/docs/src/claude-code-language-model.test.ts +3883 -0
- package/docs/src/claude-code-language-model.ts +2586 -0
- package/docs/src/claude-code-provider.test.ts +97 -0
- package/docs/src/claude-code-provider.ts +179 -0
- package/docs/src/convert-to-claude-code-messages.images.test.ts +104 -0
- package/docs/src/convert-to-claude-code-messages.test.ts +193 -0
- package/docs/src/convert-to-claude-code-messages.ts +419 -0
- package/docs/src/errors.test.ts +213 -0
- package/docs/src/errors.ts +216 -0
- package/docs/src/index.test.ts +49 -0
- package/docs/src/index.ts +98 -0
- package/docs/src/logger.integration.test.ts +164 -0
- package/docs/src/logger.test.ts +184 -0
- package/docs/src/logger.ts +65 -0
- package/docs/src/map-claude-code-finish-reason.test.ts +120 -0
- package/docs/src/map-claude-code-finish-reason.ts +60 -0
- package/docs/src/mcp-helpers.test.ts +71 -0
- package/docs/src/mcp-helpers.ts +123 -0
- package/docs/src/message-injection.test.ts +460 -0
- package/docs/src/types.ts +447 -0
- package/docs/src/validation.test.ts +558 -0
- package/docs/src/validation.ts +360 -0
- package/package.json +124 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { createClaudeCode } from './claude-code-provider.js';
|
|
3
|
+
import { ClaudeCodeLanguageModel } from './claude-code-language-model.js';
|
|
4
|
+
|
|
5
|
+
describe('createClaudeCode', () => {
|
|
6
|
+
it('should create a provider with default settings', () => {
|
|
7
|
+
const provider = createClaudeCode();
|
|
8
|
+
expect(provider).toBeDefined();
|
|
9
|
+
expect(typeof provider).toBe('function');
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('should create a provider with custom settings', () => {
|
|
13
|
+
const customSettings = {
|
|
14
|
+
pathToClaudeCodeExecutable: '/custom/path/claude',
|
|
15
|
+
customSystemPrompt: 'Custom prompt',
|
|
16
|
+
maxTurns: 10,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const provider = createClaudeCode({ defaultSettings: customSettings });
|
|
20
|
+
expect(provider).toBeDefined();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should return a language model when called with opus', () => {
|
|
24
|
+
const provider = createClaudeCode();
|
|
25
|
+
const model = provider('opus');
|
|
26
|
+
|
|
27
|
+
expect(model).toBeInstanceOf(ClaudeCodeLanguageModel);
|
|
28
|
+
expect(model.modelId).toBe('opus');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should return a language model when called with sonnet', () => {
|
|
32
|
+
const provider = createClaudeCode();
|
|
33
|
+
const model = provider('sonnet');
|
|
34
|
+
|
|
35
|
+
expect(model).toBeInstanceOf(ClaudeCodeLanguageModel);
|
|
36
|
+
expect(model.modelId).toBe('sonnet');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should allow custom model IDs', () => {
|
|
40
|
+
const provider = createClaudeCode();
|
|
41
|
+
const model = provider('custom-model-id');
|
|
42
|
+
|
|
43
|
+
expect(model).toBeInstanceOf(ClaudeCodeLanguageModel);
|
|
44
|
+
expect(model.modelId).toBe('custom-model-id');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should merge provider settings with model settings', () => {
|
|
48
|
+
const providerSettings = {
|
|
49
|
+
pathToClaudeCodeExecutable: '/provider/path',
|
|
50
|
+
maxTurns: 5,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const modelSettings = {
|
|
54
|
+
maxTurns: 10,
|
|
55
|
+
customSystemPrompt: 'Model prompt',
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const provider = createClaudeCode({ defaultSettings: providerSettings });
|
|
59
|
+
const model = provider('opus', modelSettings);
|
|
60
|
+
|
|
61
|
+
expect(model).toBeInstanceOf(ClaudeCodeLanguageModel);
|
|
62
|
+
// Model settings should override provider settings
|
|
63
|
+
expect((model as ClaudeCodeLanguageModel).settings.maxTurns).toBe(10);
|
|
64
|
+
expect((model as ClaudeCodeLanguageModel).settings.customSystemPrompt).toBe('Model prompt');
|
|
65
|
+
expect((model as ClaudeCodeLanguageModel).settings.pathToClaudeCodeExecutable).toBe(
|
|
66
|
+
'/provider/path'
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should create model with custom settings', () => {
|
|
71
|
+
const provider = createClaudeCode();
|
|
72
|
+
const model = provider('sonnet', { resume: 'test-session-123' });
|
|
73
|
+
|
|
74
|
+
expect(model).toBeInstanceOf(ClaudeCodeLanguageModel);
|
|
75
|
+
expect((model as ClaudeCodeLanguageModel).settings.resume).toBe('test-session-123');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should work with destructured usage', () => {
|
|
79
|
+
const { claudeCode } = { claudeCode: createClaudeCode() };
|
|
80
|
+
const model = claudeCode('opus');
|
|
81
|
+
|
|
82
|
+
expect(model).toBeInstanceOf(ClaudeCodeLanguageModel);
|
|
83
|
+
expect(model.modelId).toBe('opus');
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('claudeCode export', () => {
|
|
88
|
+
it('should export a default provider instance', async () => {
|
|
89
|
+
const { claudeCode } = await import('./claude-code-provider.js');
|
|
90
|
+
|
|
91
|
+
expect(claudeCode).toBeDefined();
|
|
92
|
+
expect(typeof claudeCode).toBe('function');
|
|
93
|
+
|
|
94
|
+
const model = claudeCode('sonnet');
|
|
95
|
+
expect(model).toBeInstanceOf(ClaudeCodeLanguageModel);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import type { LanguageModelV3, ProviderV3 } from '@ai-sdk/provider';
|
|
2
|
+
import { NoSuchModelError } from '@ai-sdk/provider';
|
|
3
|
+
import { ClaudeCodeLanguageModel, type ClaudeCodeModelId } from './claude-code-language-model.js';
|
|
4
|
+
import type { ClaudeCodeSettings } from './types.js';
|
|
5
|
+
import { validateSettings } from './validation.js';
|
|
6
|
+
import { getLogger } from './logger.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Claude Code provider interface that extends the AI SDK's ProviderV3.
|
|
10
|
+
* Provides methods to create language models for interacting with Claude via the CLI.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* import { claudeCode } from '@boostecom/provider';
|
|
15
|
+
*
|
|
16
|
+
* // Create a model instance
|
|
17
|
+
* const model = claudeCode('opus');
|
|
18
|
+
*
|
|
19
|
+
* // Or use the explicit methods
|
|
20
|
+
* const chatModel = claudeCode.chat('sonnet');
|
|
21
|
+
* const languageModel = claudeCode.languageModel('opus', { maxTurns: 10 });
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export interface ClaudeCodeProvider extends ProviderV3 {
|
|
25
|
+
/**
|
|
26
|
+
* Creates a language model instance for the specified model ID.
|
|
27
|
+
* This is a shorthand for calling `languageModel()`.
|
|
28
|
+
*
|
|
29
|
+
* @param modelId - The Claude model to use ('opus' or 'sonnet')
|
|
30
|
+
* @param settings - Optional settings to configure the model
|
|
31
|
+
* @returns A language model instance
|
|
32
|
+
*/
|
|
33
|
+
(modelId: ClaudeCodeModelId, settings?: ClaudeCodeSettings): LanguageModelV3;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Creates a language model instance for text generation.
|
|
37
|
+
*
|
|
38
|
+
* @param modelId - The Claude model to use ('opus' or 'sonnet')
|
|
39
|
+
* @param settings - Optional settings to configure the model
|
|
40
|
+
* @returns A language model instance
|
|
41
|
+
*/
|
|
42
|
+
languageModel(modelId: ClaudeCodeModelId, settings?: ClaudeCodeSettings): LanguageModelV3;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Alias for `languageModel()` to maintain compatibility with AI SDK patterns.
|
|
46
|
+
*
|
|
47
|
+
* @param modelId - The Claude model to use ('opus' or 'sonnet')
|
|
48
|
+
* @param settings - Optional settings to configure the model
|
|
49
|
+
* @returns A language model instance
|
|
50
|
+
*/
|
|
51
|
+
chat(modelId: ClaudeCodeModelId, settings?: ClaudeCodeSettings): LanguageModelV3;
|
|
52
|
+
|
|
53
|
+
imageModel(modelId: string): never;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Configuration options for creating a Claude Code provider instance.
|
|
58
|
+
* These settings will be applied as defaults to all models created by the provider.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* const provider = createClaudeCode({
|
|
63
|
+
* defaultSettings: {
|
|
64
|
+
* maxTurns: 5,
|
|
65
|
+
* cwd: '/path/to/project'
|
|
66
|
+
* }
|
|
67
|
+
* });
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export interface ClaudeCodeProviderSettings {
|
|
71
|
+
/**
|
|
72
|
+
* Default settings to use for all models created by this provider.
|
|
73
|
+
* Individual model settings will override these defaults.
|
|
74
|
+
*/
|
|
75
|
+
defaultSettings?: ClaudeCodeSettings;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Creates a Claude Code provider instance with the specified configuration.
|
|
80
|
+
* The provider can be used to create language models for interacting with Claude 4 models.
|
|
81
|
+
*
|
|
82
|
+
* @param options - Provider configuration options
|
|
83
|
+
* @returns Claude Code provider instance
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```typescript
|
|
87
|
+
* const provider = createClaudeCode({
|
|
88
|
+
* defaultSettings: {
|
|
89
|
+
* permissionMode: 'bypassPermissions',
|
|
90
|
+
* maxTurns: 10
|
|
91
|
+
* }
|
|
92
|
+
* });
|
|
93
|
+
*
|
|
94
|
+
* const model = provider('opus');
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export function createClaudeCode(options: ClaudeCodeProviderSettings = {}): ClaudeCodeProvider {
|
|
98
|
+
// Get logger from default settings if provided
|
|
99
|
+
const logger = getLogger(options.defaultSettings?.logger);
|
|
100
|
+
|
|
101
|
+
// Validate default settings if provided
|
|
102
|
+
if (options.defaultSettings) {
|
|
103
|
+
const validation = validateSettings(options.defaultSettings);
|
|
104
|
+
if (!validation.valid) {
|
|
105
|
+
throw new Error(`Invalid default settings: ${validation.errors.join(', ')}`);
|
|
106
|
+
}
|
|
107
|
+
if (validation.warnings.length > 0) {
|
|
108
|
+
validation.warnings.forEach((warning) => logger.warn(`Claude Code Provider: ${warning}`));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const createModel = (
|
|
113
|
+
modelId: ClaudeCodeModelId,
|
|
114
|
+
settings: ClaudeCodeSettings = {}
|
|
115
|
+
): LanguageModelV3 => {
|
|
116
|
+
const mergedSettings = {
|
|
117
|
+
...options.defaultSettings,
|
|
118
|
+
...settings,
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// Validate merged settings
|
|
122
|
+
const validation = validateSettings(mergedSettings);
|
|
123
|
+
if (!validation.valid) {
|
|
124
|
+
throw new Error(`Invalid settings: ${validation.errors.join(', ')}`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return new ClaudeCodeLanguageModel({
|
|
128
|
+
id: modelId,
|
|
129
|
+
settings: mergedSettings,
|
|
130
|
+
settingsValidationWarnings: validation.warnings,
|
|
131
|
+
});
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const provider = function (modelId: ClaudeCodeModelId, settings?: ClaudeCodeSettings) {
|
|
135
|
+
if (new.target) {
|
|
136
|
+
throw new Error('The Claude Code model function cannot be called with the new keyword.');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return createModel(modelId, settings);
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
provider.languageModel = createModel;
|
|
143
|
+
provider.chat = createModel; // Alias for languageModel
|
|
144
|
+
provider.specificationVersion = 'v3' as const;
|
|
145
|
+
|
|
146
|
+
// Add embeddingModel method that throws NoSuchModelError
|
|
147
|
+
provider.embeddingModel = (modelId: string) => {
|
|
148
|
+
throw new NoSuchModelError({
|
|
149
|
+
modelId,
|
|
150
|
+
modelType: 'embeddingModel',
|
|
151
|
+
});
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
provider.imageModel = (modelId: string) => {
|
|
155
|
+
throw new NoSuchModelError({
|
|
156
|
+
modelId,
|
|
157
|
+
modelType: 'imageModel',
|
|
158
|
+
});
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
return provider as ClaudeCodeProvider;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Default Claude Code provider instance.
|
|
166
|
+
* Pre-configured provider for quick usage without custom settings.
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* ```typescript
|
|
170
|
+
* import { claudeCode } from '@boostecom/provider';
|
|
171
|
+
* import { generateText } from 'ai';
|
|
172
|
+
*
|
|
173
|
+
* const { text } = await generateText({
|
|
174
|
+
* model: claudeCode('sonnet'),
|
|
175
|
+
* prompt: 'Hello, Claude!'
|
|
176
|
+
* });
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
export const claudeCode = createClaudeCode();
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import type { ModelMessage } from 'ai';
|
|
3
|
+
import { convertToClaudeCodeMessages } from './convert-to-claude-code-messages.js';
|
|
4
|
+
|
|
5
|
+
describe('convertToClaudeCodeMessages (images)', () => {
|
|
6
|
+
it('includes data URL images in streaming content', () => {
|
|
7
|
+
const prompt = [
|
|
8
|
+
{
|
|
9
|
+
role: 'user',
|
|
10
|
+
content: [
|
|
11
|
+
{ type: 'text', text: 'Here is a sample image.' },
|
|
12
|
+
{ type: 'image', image: 'data:image/png;base64,aGVsbG8=' },
|
|
13
|
+
],
|
|
14
|
+
},
|
|
15
|
+
] as ModelMessage[];
|
|
16
|
+
|
|
17
|
+
const result = convertToClaudeCodeMessages(prompt);
|
|
18
|
+
|
|
19
|
+
expect(result.warnings).toBeUndefined();
|
|
20
|
+
expect(result.hasImageParts).toBe(true);
|
|
21
|
+
expect(result.streamingContentParts).toHaveLength(2);
|
|
22
|
+
expect(result.streamingContentParts[0]).toEqual({
|
|
23
|
+
type: 'text',
|
|
24
|
+
text: 'Human: Here is a sample image.',
|
|
25
|
+
});
|
|
26
|
+
expect(result.streamingContentParts[1]).toEqual({
|
|
27
|
+
type: 'image',
|
|
28
|
+
source: {
|
|
29
|
+
type: 'base64',
|
|
30
|
+
media_type: 'image/png',
|
|
31
|
+
data: 'aGVsbG8=',
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('includes base64 images when mimeType is provided', () => {
|
|
37
|
+
const prompt = [
|
|
38
|
+
{
|
|
39
|
+
role: 'user',
|
|
40
|
+
content: [
|
|
41
|
+
{ type: 'text', text: 'Inline base64 image.' },
|
|
42
|
+
{ type: 'image', image: { data: 'AQID', mimeType: 'image/jpeg' } },
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
] as ModelMessage[];
|
|
46
|
+
|
|
47
|
+
const result = convertToClaudeCodeMessages(prompt);
|
|
48
|
+
|
|
49
|
+
expect(result.warnings).toBeUndefined();
|
|
50
|
+
expect(result.hasImageParts).toBe(true);
|
|
51
|
+
expect(result.streamingContentParts[1]).toEqual({
|
|
52
|
+
type: 'image',
|
|
53
|
+
source: {
|
|
54
|
+
type: 'base64',
|
|
55
|
+
media_type: 'image/jpeg',
|
|
56
|
+
data: 'AQID',
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('warns and skips HTTP image URLs', () => {
|
|
62
|
+
const prompt = [
|
|
63
|
+
{
|
|
64
|
+
role: 'user',
|
|
65
|
+
content: [
|
|
66
|
+
{ type: 'text', text: 'Remote image' },
|
|
67
|
+
{ type: 'image', image: 'https://example.com/image.png' },
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
] as ModelMessage[];
|
|
71
|
+
|
|
72
|
+
const result = convertToClaudeCodeMessages(prompt);
|
|
73
|
+
|
|
74
|
+
expect(result.hasImageParts).toBe(false);
|
|
75
|
+
expect(result.warnings).toContain(
|
|
76
|
+
'Image URLs are not supported by this provider; supply base64/data URLs.'
|
|
77
|
+
);
|
|
78
|
+
expect(result.streamingContentParts).toHaveLength(1);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('accepts file parts with image media type', () => {
|
|
82
|
+
const prompt = [
|
|
83
|
+
{
|
|
84
|
+
role: 'user',
|
|
85
|
+
content: [
|
|
86
|
+
{ type: 'text', text: 'File part image.' },
|
|
87
|
+
{ type: 'file', mediaType: 'image/png', data: 'aGVsbG8=' },
|
|
88
|
+
],
|
|
89
|
+
},
|
|
90
|
+
] as ModelMessage[];
|
|
91
|
+
|
|
92
|
+
const result = convertToClaudeCodeMessages(prompt);
|
|
93
|
+
|
|
94
|
+
expect(result.hasImageParts).toBe(true);
|
|
95
|
+
expect(result.streamingContentParts[1]).toEqual({
|
|
96
|
+
type: 'image',
|
|
97
|
+
source: {
|
|
98
|
+
type: 'base64',
|
|
99
|
+
media_type: 'image/png',
|
|
100
|
+
data: 'aGVsbG8=',
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
});
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { convertToClaudeCodeMessages } from './convert-to-claude-code-messages.js';
|
|
3
|
+
import type { ModelMessage } from 'ai';
|
|
4
|
+
|
|
5
|
+
describe('convertToClaudeCodeMessages', () => {
|
|
6
|
+
it('should convert a simple user message', () => {
|
|
7
|
+
const result = convertToClaudeCodeMessages([
|
|
8
|
+
{ role: 'user', content: 'Hello, Claude!' },
|
|
9
|
+
] as ModelMessage[]);
|
|
10
|
+
|
|
11
|
+
expect(result.messagesPrompt).toBe('Human: Hello, Claude!');
|
|
12
|
+
expect(result.systemPrompt).toBeUndefined();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('should convert a simple assistant message', () => {
|
|
16
|
+
const result = convertToClaudeCodeMessages([
|
|
17
|
+
{ role: 'assistant', content: 'Hello! How can I help you?' },
|
|
18
|
+
] as ModelMessage[]);
|
|
19
|
+
|
|
20
|
+
expect(result.messagesPrompt).toBe('Assistant: Hello! How can I help you?');
|
|
21
|
+
expect(result.systemPrompt).toBeUndefined();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should handle system message', () => {
|
|
25
|
+
const result = convertToClaudeCodeMessages([
|
|
26
|
+
{ role: 'system', content: 'You are a helpful assistant.' },
|
|
27
|
+
] as ModelMessage[]);
|
|
28
|
+
|
|
29
|
+
expect(result.messagesPrompt).toBe('You are a helpful assistant.');
|
|
30
|
+
expect(result.systemPrompt).toBe('You are a helpful assistant.');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should handle a conversation with multiple messages', () => {
|
|
34
|
+
const result = convertToClaudeCodeMessages([
|
|
35
|
+
{ role: 'system', content: 'Be helpful.' },
|
|
36
|
+
{ role: 'user', content: 'What is 2+2?' },
|
|
37
|
+
{ role: 'assistant', content: '2+2 equals 4.' },
|
|
38
|
+
{ role: 'user', content: 'Thanks!' },
|
|
39
|
+
] as ModelMessage[]);
|
|
40
|
+
|
|
41
|
+
expect(result.systemPrompt).toBe('Be helpful.');
|
|
42
|
+
expect(result.messagesPrompt).toBe(
|
|
43
|
+
'Be helpful.\n\nHuman: What is 2+2?\n\nAssistant: 2+2 equals 4.\n\nHuman: Thanks!'
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should handle multi-part text messages', () => {
|
|
48
|
+
const result = convertToClaudeCodeMessages([
|
|
49
|
+
{
|
|
50
|
+
role: 'user',
|
|
51
|
+
content: [
|
|
52
|
+
{ type: 'text', text: 'Hello' },
|
|
53
|
+
{ type: 'text', text: ', ' },
|
|
54
|
+
{ type: 'text', text: 'world!' },
|
|
55
|
+
],
|
|
56
|
+
},
|
|
57
|
+
] as ModelMessage[]);
|
|
58
|
+
|
|
59
|
+
expect(result.messagesPrompt).toBe('Human: Hello\n, \nworld!');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should return warning when image content cannot be converted', () => {
|
|
63
|
+
const result = convertToClaudeCodeMessages([
|
|
64
|
+
{
|
|
65
|
+
role: 'user',
|
|
66
|
+
content: [
|
|
67
|
+
{ type: 'text', text: 'Look at this:' },
|
|
68
|
+
{ type: 'image', image: new Uint8Array([1, 2, 3]) },
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
] as ModelMessage[]);
|
|
72
|
+
|
|
73
|
+
expect(result.warnings).toBeDefined();
|
|
74
|
+
expect(result.warnings).toContain('Unable to convert image content; supply base64/data URLs.');
|
|
75
|
+
expect(result.messagesPrompt).toBe('Human: Look at this:');
|
|
76
|
+
expect(result.hasImageParts).toBe(false);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('should handle unknown content types gracefully', () => {
|
|
80
|
+
const result = convertToClaudeCodeMessages([
|
|
81
|
+
{
|
|
82
|
+
role: 'user',
|
|
83
|
+
content: [
|
|
84
|
+
{ type: 'text', text: 'Check this file:' },
|
|
85
|
+
{ type: 'file', data: 'data:text/plain;base64,AQID', mimeType: 'text/plain' },
|
|
86
|
+
],
|
|
87
|
+
},
|
|
88
|
+
] as any);
|
|
89
|
+
|
|
90
|
+
// Unknown content types are filtered out
|
|
91
|
+
expect(result.messagesPrompt).toBe('Human: Check this file:');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should convert tool messages', () => {
|
|
95
|
+
const result = convertToClaudeCodeMessages([
|
|
96
|
+
{
|
|
97
|
+
role: 'tool',
|
|
98
|
+
content: [
|
|
99
|
+
{
|
|
100
|
+
type: 'tool-result',
|
|
101
|
+
toolCallId: 'call-123',
|
|
102
|
+
toolName: 'calculator',
|
|
103
|
+
output: { type: 'json', value: { answer: 42 } },
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
},
|
|
107
|
+
] as any);
|
|
108
|
+
|
|
109
|
+
expect(result.messagesPrompt).toBe('Tool Result (calculator): {"answer":42}');
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should handle tool error messages', () => {
|
|
113
|
+
const result = convertToClaudeCodeMessages([
|
|
114
|
+
{
|
|
115
|
+
role: 'tool',
|
|
116
|
+
content: [
|
|
117
|
+
{
|
|
118
|
+
type: 'tool-result',
|
|
119
|
+
toolCallId: 'call-456',
|
|
120
|
+
toolName: 'search',
|
|
121
|
+
output: { type: 'text', value: 'Network error' },
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
},
|
|
125
|
+
] as any);
|
|
126
|
+
|
|
127
|
+
expect(result.messagesPrompt).toBe('Tool Result (search): Network error');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should handle empty content array', () => {
|
|
131
|
+
const result = convertToClaudeCodeMessages([
|
|
132
|
+
{
|
|
133
|
+
role: 'user',
|
|
134
|
+
content: [],
|
|
135
|
+
},
|
|
136
|
+
] as ModelMessage[]);
|
|
137
|
+
|
|
138
|
+
expect(result.messagesPrompt).toBe('');
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('should handle undefined content gracefully', () => {
|
|
142
|
+
const result = convertToClaudeCodeMessages([
|
|
143
|
+
{
|
|
144
|
+
role: 'user',
|
|
145
|
+
content: [{ type: 'text', text: undefined as any }],
|
|
146
|
+
},
|
|
147
|
+
] as ModelMessage[]);
|
|
148
|
+
|
|
149
|
+
expect(result.messagesPrompt).toBe('');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('should handle complex tool results', () => {
|
|
153
|
+
const result = convertToClaudeCodeMessages([
|
|
154
|
+
{
|
|
155
|
+
role: 'tool',
|
|
156
|
+
content: [
|
|
157
|
+
{
|
|
158
|
+
type: 'tool-result',
|
|
159
|
+
toolCallId: 'call-789',
|
|
160
|
+
toolName: 'database',
|
|
161
|
+
output: {
|
|
162
|
+
type: 'json',
|
|
163
|
+
value: {
|
|
164
|
+
users: [
|
|
165
|
+
{ id: 1, name: 'Alice' },
|
|
166
|
+
{ id: 2, name: 'Bob' },
|
|
167
|
+
],
|
|
168
|
+
count: 2,
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
},
|
|
174
|
+
] as any);
|
|
175
|
+
|
|
176
|
+
expect(result.messagesPrompt).toBe(
|
|
177
|
+
'Tool Result (database): {"users":[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}],"count":2}'
|
|
178
|
+
);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('should handle consecutive messages properly', () => {
|
|
182
|
+
const result = convertToClaudeCodeMessages([
|
|
183
|
+
{ role: 'user', content: 'First message' },
|
|
184
|
+
{ role: 'user', content: 'Second message' },
|
|
185
|
+
{ role: 'assistant', content: 'Response' },
|
|
186
|
+
{ role: 'user', content: 'Third message' },
|
|
187
|
+
] as ModelMessage[]);
|
|
188
|
+
|
|
189
|
+
expect(result.messagesPrompt).toBe(
|
|
190
|
+
'Human: First message\n\nHuman: Second message\n\nAssistant: Response\n\nHuman: Third message'
|
|
191
|
+
);
|
|
192
|
+
});
|
|
193
|
+
});
|