conversationalist 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/LICENSE +21 -0
- package/README.md +369 -0
- package/dist/adapters/anthropic/index.d.ts +76 -0
- package/dist/adapters/anthropic/index.d.ts.map +1 -0
- package/dist/adapters/anthropic/index.js +147 -0
- package/dist/adapters/anthropic/index.js.map +10 -0
- package/dist/adapters/gemini/index.d.ts +79 -0
- package/dist/adapters/gemini/index.d.ts.map +1 -0
- package/dist/adapters/gemini/index.js +148 -0
- package/dist/adapters/gemini/index.js.map +10 -0
- package/dist/adapters/openai/index.d.ts +65 -0
- package/dist/adapters/openai/index.d.ts.map +1 -0
- package/dist/adapters/openai/index.js +127 -0
- package/dist/adapters/openai/index.js.map +10 -0
- package/dist/context.d.ts +35 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/conversation/append.d.ts +25 -0
- package/dist/conversation/append.d.ts.map +1 -0
- package/dist/conversation/create.d.ts +14 -0
- package/dist/conversation/create.d.ts.map +1 -0
- package/dist/conversation/index.d.ts +9 -0
- package/dist/conversation/index.d.ts.map +1 -0
- package/dist/conversation/modify.d.ts +9 -0
- package/dist/conversation/modify.d.ts.map +1 -0
- package/dist/conversation/query.d.ts +34 -0
- package/dist/conversation/query.d.ts.map +1 -0
- package/dist/conversation/serialization.d.ts +13 -0
- package/dist/conversation/serialization.d.ts.map +1 -0
- package/dist/conversation/system-messages.d.ts +31 -0
- package/dist/conversation/system-messages.d.ts.map +1 -0
- package/dist/conversation/tool-tracking.d.ts +26 -0
- package/dist/conversation/tool-tracking.d.ts.map +1 -0
- package/dist/conversation/transform.d.ts +9 -0
- package/dist/conversation/transform.d.ts.map +1 -0
- package/dist/conversation.d.ts +39 -0
- package/dist/conversation.d.ts.map +1 -0
- package/dist/environment.d.ts +23 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/errors.d.ts +67 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14378 -0
- package/dist/index.js.map +87 -0
- package/dist/message.d.ts +2 -0
- package/dist/message.d.ts.map +1 -0
- package/dist/multi-modal.d.ts +27 -0
- package/dist/multi-modal.d.ts.map +1 -0
- package/dist/schemas.d.ts +30 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/streaming.d.ts +37 -0
- package/dist/streaming.d.ts.map +1 -0
- package/dist/types.d.ts +78 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utilities.d.ts +79 -0
- package/dist/utilities.d.ts.map +1 -0
- package/dist/with-conversation.d.ts +125 -0
- package/dist/with-conversation.d.ts.map +1 -0
- package/package.json +113 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Steve Kinney
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
# Conversationalist
|
|
2
|
+
|
|
3
|
+
A TypeScript library for managing LLM conversation state with immutable data structures and type-safe APIs.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add conversationalist
|
|
9
|
+
npm add conversationalist
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Core Concepts
|
|
13
|
+
|
|
14
|
+
### Conversation
|
|
15
|
+
|
|
16
|
+
A conversation is an immutable data structure containing messages, metadata, and timestamps:
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import {
|
|
20
|
+
createConversation,
|
|
21
|
+
appendUserMessage,
|
|
22
|
+
appendAssistantMessage,
|
|
23
|
+
} from 'conversationalist';
|
|
24
|
+
|
|
25
|
+
let conversation = createConversation({ title: 'My Chat' });
|
|
26
|
+
|
|
27
|
+
conversation = appendUserMessage(conversation, 'Hello!');
|
|
28
|
+
conversation = appendAssistantMessage(conversation, 'Hi there!');
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Messages
|
|
32
|
+
|
|
33
|
+
Messages have roles (`user`, `assistant`, `system`, `tool-use`, `tool-result`, `developer`, `snapshot`) and can contain text or multi-modal content:
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { appendMessages } from 'conversationalist';
|
|
37
|
+
|
|
38
|
+
// Text message
|
|
39
|
+
conversation = appendMessages(conversation, {
|
|
40
|
+
role: 'user',
|
|
41
|
+
content: 'What is in this image?',
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// Multi-modal message with image
|
|
45
|
+
conversation = appendMessages(conversation, {
|
|
46
|
+
role: 'user',
|
|
47
|
+
content: [
|
|
48
|
+
{ type: 'text', text: 'Describe this:' },
|
|
49
|
+
{ type: 'image', url: 'https://example.com/image.png' },
|
|
50
|
+
],
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Tool Calls
|
|
55
|
+
|
|
56
|
+
Tool use is supported with linked tool calls and results:
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
conversation = appendMessages(
|
|
60
|
+
conversation,
|
|
61
|
+
{
|
|
62
|
+
role: 'tool-use',
|
|
63
|
+
content: '',
|
|
64
|
+
toolCall: { id: 'call_123', name: 'get_weather', arguments: '{"city":"NYC"}' },
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
role: 'tool-result',
|
|
68
|
+
content: '',
|
|
69
|
+
toolResult: { callId: 'call_123', outcome: 'success', content: '72°F, sunny' },
|
|
70
|
+
},
|
|
71
|
+
);
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## API Reference
|
|
75
|
+
|
|
76
|
+
### Creating Conversations
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
createConversation(options?: {
|
|
80
|
+
id?: string;
|
|
81
|
+
title?: string;
|
|
82
|
+
status?: 'active' | 'archived' | 'deleted';
|
|
83
|
+
metadata?: Record<string, unknown>;
|
|
84
|
+
tags?: string[];
|
|
85
|
+
}): Conversation
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Appending Messages
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
appendMessages(conversation: Conversation, ...inputs: MessageInput[]): Conversation
|
|
92
|
+
appendUserMessage(conversation: Conversation, content: string | MultiModalContent[]): Conversation
|
|
93
|
+
appendAssistantMessage(conversation: Conversation, content: string | MultiModalContent[]): Conversation
|
|
94
|
+
appendSystemMessage(conversation: Conversation, content: string): Conversation
|
|
95
|
+
prependSystemMessage(conversation: Conversation, content: string): Conversation
|
|
96
|
+
replaceSystemMessage(conversation: Conversation, content: string): Conversation
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Querying Messages
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
getConversationMessages(conversation: Conversation, options?: { includeHidden?: boolean }): Message[]
|
|
103
|
+
getMessageAtPosition(conversation: Conversation, position: number): Message | undefined
|
|
104
|
+
getMessageByIdentifier(conversation: Conversation, id: string): Message | undefined
|
|
105
|
+
searchConversationMessages(conversation: Conversation, predicate: (m: Message) => boolean): Message[]
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### System Messages
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
hasSystemMessage(conversation: Conversation): boolean
|
|
112
|
+
getFirstSystemMessage(conversation: Conversation): Message | undefined
|
|
113
|
+
getSystemMessages(conversation: Conversation): Message[]
|
|
114
|
+
collapseSystemMessages(conversation: Conversation): Conversation
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Utilities
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
computeConversationStatistics(conversation: Conversation): {
|
|
121
|
+
total: number;
|
|
122
|
+
byRole: Record<string, number>;
|
|
123
|
+
hidden: number;
|
|
124
|
+
withImages: number;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
redactMessageAtPosition(conversation: Conversation, position: number, placeholder?: string): Conversation
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Serialization
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
serializeConversation(conversation: Conversation): ConversationJSON
|
|
134
|
+
deserializeConversation(json: ConversationJSON): Conversation
|
|
135
|
+
toChatMessages(conversation: Conversation): ExternalMessage[]
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Builder Pattern
|
|
139
|
+
|
|
140
|
+
For fluent API style:
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
import { createConversation } from 'conversationalist';
|
|
144
|
+
import { withConversation, pipeConversation } from 'conversationalist';
|
|
145
|
+
import { simpleTokenEstimator } from 'conversationalist';
|
|
146
|
+
|
|
147
|
+
// Draft pattern with callback
|
|
148
|
+
const conversation = withConversation(createConversation(), (draft) => {
|
|
149
|
+
draft
|
|
150
|
+
.appendSystemMessage('You are a helpful assistant.')
|
|
151
|
+
.appendUserMessage('Hello!')
|
|
152
|
+
.appendAssistantMessage('Hi there!');
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// Streaming with the draft pattern
|
|
156
|
+
const streamedConversation = withConversation(createConversation(), (draft) => {
|
|
157
|
+
const { draft: d, messageId } = draft.appendStreamingMessage('assistant');
|
|
158
|
+
d.updateStreamingMessage(messageId, 'Partial response...').finalizeStreamingMessage(
|
|
159
|
+
messageId,
|
|
160
|
+
{ tokenUsage: { prompt: 10, completion: 5, total: 15 } },
|
|
161
|
+
);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// Context window management with the draft pattern
|
|
165
|
+
const truncatedConversation = withConversation(conversation, (draft) => {
|
|
166
|
+
draft.truncateToTokenLimit(4000, simpleTokenEstimator, { preserveLastN: 2 });
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// Pipe pattern
|
|
170
|
+
const conversation = pipeConversation(
|
|
171
|
+
createConversation(),
|
|
172
|
+
(c) => appendSystemMessage(c, 'You are helpful.'),
|
|
173
|
+
(c) => appendUserMessage(c, 'Hello!'),
|
|
174
|
+
);
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
The `ConversationDraft` provides all conversation manipulation methods:
|
|
178
|
+
|
|
179
|
+
- **Message appending**: `appendMessages`, `appendUserMessage`, `appendAssistantMessage`, `appendSystemMessage`
|
|
180
|
+
- **System messages**: `prependSystemMessage`, `replaceSystemMessage`, `collapseSystemMessages`
|
|
181
|
+
- **Modification**: `redactMessageAtPosition`
|
|
182
|
+
- **Streaming**: `appendStreamingMessage`, `updateStreamingMessage`, `finalizeStreamingMessage`, `cancelStreamingMessage`
|
|
183
|
+
- **Context window**: `truncateFromPosition`, `truncateToTokenLimit`
|
|
184
|
+
|
|
185
|
+
### Tool Call Pairing
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
import { pairToolCallsWithResults } from 'conversationalist';
|
|
189
|
+
|
|
190
|
+
const pairs = pairToolCallsWithResults(conversation.messages);
|
|
191
|
+
// Returns: [{ call: ToolCall, result?: ToolResult }, ...]
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Provider Adapters
|
|
195
|
+
|
|
196
|
+
Convert conversations to provider-specific formats using subpath exports:
|
|
197
|
+
|
|
198
|
+
### OpenAI
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
import { toOpenAIMessages } from 'conversationalist/openai';
|
|
202
|
+
|
|
203
|
+
const messages = toOpenAIMessages(conversation);
|
|
204
|
+
const response = await openai.chat.completions.create({
|
|
205
|
+
model: 'gpt-4',
|
|
206
|
+
messages,
|
|
207
|
+
});
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Anthropic
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
import { toAnthropicMessages } from 'conversationalist/anthropic';
|
|
214
|
+
|
|
215
|
+
const { system, messages } = toAnthropicMessages(conversation);
|
|
216
|
+
const response = await anthropic.messages.create({
|
|
217
|
+
model: 'claude-3-opus-20240229',
|
|
218
|
+
system,
|
|
219
|
+
messages,
|
|
220
|
+
});
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Google Gemini
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
import { toGeminiMessages } from 'conversationalist/gemini';
|
|
227
|
+
|
|
228
|
+
const { systemInstruction, contents } = toGeminiMessages(conversation);
|
|
229
|
+
const response = await model.generateContent({
|
|
230
|
+
systemInstruction,
|
|
231
|
+
contents,
|
|
232
|
+
});
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Streaming Support
|
|
236
|
+
|
|
237
|
+
Handle streaming responses with pending message utilities:
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
import {
|
|
241
|
+
appendStreamingMessage,
|
|
242
|
+
updateStreamingMessage,
|
|
243
|
+
finalizeStreamingMessage,
|
|
244
|
+
cancelStreamingMessage,
|
|
245
|
+
isStreamingMessage,
|
|
246
|
+
getStreamingMessage,
|
|
247
|
+
} from 'conversationalist';
|
|
248
|
+
|
|
249
|
+
// Start a streaming message
|
|
250
|
+
let { conversation, messageId } = appendStreamingMessage(conversation, 'assistant');
|
|
251
|
+
|
|
252
|
+
// Update content as chunks arrive
|
|
253
|
+
for await (const chunk of stream) {
|
|
254
|
+
accumulatedContent += chunk;
|
|
255
|
+
conversation = updateStreamingMessage(conversation, messageId, accumulatedContent);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Finalize when complete
|
|
259
|
+
conversation = finalizeStreamingMessage(conversation, messageId, {
|
|
260
|
+
tokenUsage: { prompt: 100, completion: 50, total: 150 },
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// Or cancel if needed
|
|
264
|
+
conversation = cancelStreamingMessage(conversation, messageId);
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
## Context Window Utilities
|
|
268
|
+
|
|
269
|
+
Manage token limits and message truncation:
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
import {
|
|
273
|
+
getRecentMessages,
|
|
274
|
+
truncateFromPosition,
|
|
275
|
+
truncateToTokenLimit,
|
|
276
|
+
estimateConversationTokens,
|
|
277
|
+
simpleTokenEstimator,
|
|
278
|
+
} from 'conversationalist';
|
|
279
|
+
|
|
280
|
+
// Get last N messages (excluding system by default)
|
|
281
|
+
const recent = getRecentMessages(conversation, 10);
|
|
282
|
+
|
|
283
|
+
// Truncate to messages from position onwards
|
|
284
|
+
const truncated = truncateFromPosition(conversation, 5);
|
|
285
|
+
|
|
286
|
+
// Truncate to fit token limit
|
|
287
|
+
const fitted = truncateToTokenLimit(conversation, 4000, simpleTokenEstimator, {
|
|
288
|
+
preserveSystemMessages: true,
|
|
289
|
+
preserveLastN: 2,
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// Estimate total tokens
|
|
293
|
+
const tokens = estimateConversationTokens(conversation, simpleTokenEstimator);
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## Types
|
|
297
|
+
|
|
298
|
+
```typescript
|
|
299
|
+
import type {
|
|
300
|
+
Conversation,
|
|
301
|
+
ConversationJSON,
|
|
302
|
+
ConversationStatus,
|
|
303
|
+
Message,
|
|
304
|
+
MessageInput,
|
|
305
|
+
MessageJSON,
|
|
306
|
+
MessageRole,
|
|
307
|
+
TokenUsage,
|
|
308
|
+
ToolCall,
|
|
309
|
+
ToolResult,
|
|
310
|
+
MultiModalContent,
|
|
311
|
+
TextContent,
|
|
312
|
+
ImageContent,
|
|
313
|
+
} from 'conversationalist';
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
## Validation Schemas
|
|
317
|
+
|
|
318
|
+
Zod schemas are exported for runtime validation:
|
|
319
|
+
|
|
320
|
+
```typescript
|
|
321
|
+
import {
|
|
322
|
+
conversationSchema,
|
|
323
|
+
messageInputSchema,
|
|
324
|
+
messageJSONSchema,
|
|
325
|
+
messageRoleSchema,
|
|
326
|
+
multiModalContentSchema,
|
|
327
|
+
tokenUsageSchema,
|
|
328
|
+
toolCallSchema,
|
|
329
|
+
toolResultSchema,
|
|
330
|
+
} from 'conversationalist';
|
|
331
|
+
|
|
332
|
+
const result = messageInputSchema.safeParse(data);
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## Error Handling
|
|
336
|
+
|
|
337
|
+
Custom error types with codes:
|
|
338
|
+
|
|
339
|
+
```typescript
|
|
340
|
+
import {
|
|
341
|
+
ConversationalistError,
|
|
342
|
+
createInvalidInputError,
|
|
343
|
+
createInvalidPositionError,
|
|
344
|
+
createNotFoundError,
|
|
345
|
+
createValidationError,
|
|
346
|
+
} from 'conversationalist';
|
|
347
|
+
|
|
348
|
+
try {
|
|
349
|
+
// ...
|
|
350
|
+
} catch (error) {
|
|
351
|
+
if (error instanceof ConversationalistError) {
|
|
352
|
+
console.log(error.code); // e.g., 'INVALID_POSITION'
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
## Development
|
|
358
|
+
|
|
359
|
+
```bash
|
|
360
|
+
bun install
|
|
361
|
+
bun test
|
|
362
|
+
bun run typecheck
|
|
363
|
+
bun run lint
|
|
364
|
+
bun run build
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
## License
|
|
368
|
+
|
|
369
|
+
MIT
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { Conversation } from '../../types';
|
|
2
|
+
/**
|
|
3
|
+
* Anthropic text content block.
|
|
4
|
+
*/
|
|
5
|
+
export interface AnthropicTextBlock {
|
|
6
|
+
type: 'text';
|
|
7
|
+
text: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Anthropic image content block.
|
|
11
|
+
*/
|
|
12
|
+
export interface AnthropicImageBlock {
|
|
13
|
+
type: 'image';
|
|
14
|
+
source: {
|
|
15
|
+
type: 'base64' | 'url';
|
|
16
|
+
media_type?: string;
|
|
17
|
+
data?: string;
|
|
18
|
+
url?: string;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Anthropic tool use content block.
|
|
23
|
+
*/
|
|
24
|
+
export interface AnthropicToolUseBlock {
|
|
25
|
+
type: 'tool_use';
|
|
26
|
+
id: string;
|
|
27
|
+
name: string;
|
|
28
|
+
input: unknown;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Anthropic tool result content block.
|
|
32
|
+
*/
|
|
33
|
+
export interface AnthropicToolResultBlock {
|
|
34
|
+
type: 'tool_result';
|
|
35
|
+
tool_use_id: string;
|
|
36
|
+
content: string;
|
|
37
|
+
is_error?: boolean;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Anthropic content block union type.
|
|
41
|
+
*/
|
|
42
|
+
export type AnthropicContentBlock = AnthropicTextBlock | AnthropicImageBlock | AnthropicToolUseBlock | AnthropicToolResultBlock;
|
|
43
|
+
/**
|
|
44
|
+
* Anthropic message format for the Messages API.
|
|
45
|
+
*/
|
|
46
|
+
export interface AnthropicMessage {
|
|
47
|
+
role: 'user' | 'assistant';
|
|
48
|
+
content: string | AnthropicContentBlock[];
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Result of converting a conversation to Anthropic format.
|
|
52
|
+
* System messages are extracted separately since Anthropic uses a top-level system parameter.
|
|
53
|
+
*/
|
|
54
|
+
export interface AnthropicConversation {
|
|
55
|
+
system?: string;
|
|
56
|
+
messages: AnthropicMessage[];
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Converts a conversation to Anthropic Messages API format.
|
|
60
|
+
* System messages are extracted to the top-level `system` field.
|
|
61
|
+
* Tool calls become tool_use blocks, tool results become tool_result blocks.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```ts
|
|
65
|
+
* import { toAnthropicMessages } from 'conversationalist/anthropic';
|
|
66
|
+
*
|
|
67
|
+
* const { system, messages } = toAnthropicMessages(conversation);
|
|
68
|
+
* const response = await anthropic.messages.create({
|
|
69
|
+
* model: 'claude-3-opus-20240229',
|
|
70
|
+
* system,
|
|
71
|
+
* messages,
|
|
72
|
+
* });
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export declare function toAnthropicMessages(conversation: Conversation): AnthropicConversation;
|
|
76
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/adapters/anthropic/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAiC,MAAM,aAAa,CAAC;AAE/E;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE;QACN,IAAI,EAAE,QAAQ,GAAG,KAAK,CAAC;QACvB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,GAAG,CAAC,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,UAAU,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,OAAO,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,aAAa,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,MAAM,qBAAqB,GAC7B,kBAAkB,GAClB,mBAAmB,GACnB,qBAAqB,GACrB,wBAAwB,CAAC;AAE7B;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,qBAAqB,EAAE,CAAC;CAC3C;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,gBAAgB,EAAE,CAAC;CAC9B;AA+GD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,YAAY,GAAG,qBAAqB,CAiFrF"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// src/adapters/anthropic/index.ts
|
|
2
|
+
function toAnthropicContent(content) {
|
|
3
|
+
if (typeof content === "string") {
|
|
4
|
+
return content;
|
|
5
|
+
}
|
|
6
|
+
const blocks = [];
|
|
7
|
+
for (const part of content) {
|
|
8
|
+
if (part.type === "text") {
|
|
9
|
+
blocks.push({ type: "text", text: part.text ?? "" });
|
|
10
|
+
} else if (part.type === "image") {
|
|
11
|
+
const url = part.url ?? "";
|
|
12
|
+
if (url.startsWith("data:")) {
|
|
13
|
+
const matches = url.match(/^data:([^;]+);base64,(.+)$/);
|
|
14
|
+
if (matches && matches[1] && matches[2]) {
|
|
15
|
+
blocks.push({
|
|
16
|
+
type: "image",
|
|
17
|
+
source: {
|
|
18
|
+
type: "base64",
|
|
19
|
+
media_type: matches[1],
|
|
20
|
+
data: matches[2]
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
} else {
|
|
25
|
+
blocks.push({
|
|
26
|
+
type: "image",
|
|
27
|
+
source: {
|
|
28
|
+
type: "url",
|
|
29
|
+
url
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return blocks.length === 1 && blocks[0]?.type === "text" ? blocks[0].text : blocks;
|
|
36
|
+
}
|
|
37
|
+
function toToolUseBlock(toolCall) {
|
|
38
|
+
return {
|
|
39
|
+
type: "tool_use",
|
|
40
|
+
id: toolCall.id,
|
|
41
|
+
name: toolCall.name,
|
|
42
|
+
input: typeof toolCall.arguments === "string" ? JSON.parse(toolCall.arguments) : toolCall.arguments
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function toToolResultBlock(toolResult) {
|
|
46
|
+
const result = {
|
|
47
|
+
type: "tool_result",
|
|
48
|
+
tool_use_id: toolResult.callId,
|
|
49
|
+
content: typeof toolResult.content === "string" ? toolResult.content : JSON.stringify(toolResult.content)
|
|
50
|
+
};
|
|
51
|
+
if (toolResult.outcome === "error") {
|
|
52
|
+
result.is_error = true;
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
function extractSystemContent(messages) {
|
|
57
|
+
const systemMessages = messages.filter((m) => (m.role === "system" || m.role === "developer") && !m.hidden);
|
|
58
|
+
if (systemMessages.length === 0) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const parts = [];
|
|
62
|
+
for (const msg of systemMessages) {
|
|
63
|
+
if (typeof msg.content === "string") {
|
|
64
|
+
parts.push(msg.content);
|
|
65
|
+
} else {
|
|
66
|
+
for (const part of msg.content) {
|
|
67
|
+
if (part.type === "text") {
|
|
68
|
+
parts.push(part.text ?? "");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return parts.join(`
|
|
74
|
+
|
|
75
|
+
`);
|
|
76
|
+
}
|
|
77
|
+
function toAnthropicMessages(conversation) {
|
|
78
|
+
const system = extractSystemContent(conversation.messages);
|
|
79
|
+
const messages = [];
|
|
80
|
+
let currentRole = null;
|
|
81
|
+
let currentBlocks = [];
|
|
82
|
+
const flushCurrent = () => {
|
|
83
|
+
if (currentRole && currentBlocks.length > 0) {
|
|
84
|
+
messages.push({
|
|
85
|
+
role: currentRole,
|
|
86
|
+
content: currentBlocks.length === 1 && currentBlocks[0]?.type === "text" ? currentBlocks[0].text : currentBlocks
|
|
87
|
+
});
|
|
88
|
+
currentBlocks = [];
|
|
89
|
+
}
|
|
90
|
+
currentRole = null;
|
|
91
|
+
};
|
|
92
|
+
for (const message of conversation.messages) {
|
|
93
|
+
if (message.hidden)
|
|
94
|
+
continue;
|
|
95
|
+
if (message.role === "system" || message.role === "developer") {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (message.role === "snapshot") {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
let targetRole;
|
|
102
|
+
let blocks = [];
|
|
103
|
+
if (message.role === "user") {
|
|
104
|
+
targetRole = "user";
|
|
105
|
+
const content = toAnthropicContent(message.content);
|
|
106
|
+
if (typeof content === "string") {
|
|
107
|
+
blocks = [{ type: "text", text: content }];
|
|
108
|
+
} else {
|
|
109
|
+
blocks = content;
|
|
110
|
+
}
|
|
111
|
+
} else if (message.role === "assistant") {
|
|
112
|
+
targetRole = "assistant";
|
|
113
|
+
const content = toAnthropicContent(message.content);
|
|
114
|
+
if (typeof content === "string") {
|
|
115
|
+
blocks = [{ type: "text", text: content }];
|
|
116
|
+
} else {
|
|
117
|
+
blocks = content;
|
|
118
|
+
}
|
|
119
|
+
} else if (message.role === "tool-use" && message.toolCall) {
|
|
120
|
+
targetRole = "assistant";
|
|
121
|
+
blocks = [toToolUseBlock(message.toolCall)];
|
|
122
|
+
} else if (message.role === "tool-result" && message.toolResult) {
|
|
123
|
+
targetRole = "user";
|
|
124
|
+
blocks = [toToolResultBlock(message.toolResult)];
|
|
125
|
+
} else {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (currentRole === targetRole) {
|
|
129
|
+
currentBlocks.push(...blocks);
|
|
130
|
+
} else {
|
|
131
|
+
flushCurrent();
|
|
132
|
+
currentRole = targetRole;
|
|
133
|
+
currentBlocks = blocks;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
flushCurrent();
|
|
137
|
+
const result = { messages };
|
|
138
|
+
if (system !== undefined) {
|
|
139
|
+
result.system = system;
|
|
140
|
+
}
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
export {
|
|
144
|
+
toAnthropicMessages
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
//# debugId=46DDFFB8D921BA7364756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/adapters/anthropic/index.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import type { MultiModalContent } from '@lasercat/homogenaize';\n\nimport type { Conversation, Message, ToolCall, ToolResult } from '../../types';\n\n/**\n * Anthropic text content block.\n */\nexport interface AnthropicTextBlock {\n type: 'text';\n text: string;\n}\n\n/**\n * Anthropic image content block.\n */\nexport interface AnthropicImageBlock {\n type: 'image';\n source: {\n type: 'base64' | 'url';\n media_type?: string;\n data?: string;\n url?: string;\n };\n}\n\n/**\n * Anthropic tool use content block.\n */\nexport interface AnthropicToolUseBlock {\n type: 'tool_use';\n id: string;\n name: string;\n input: unknown;\n}\n\n/**\n * Anthropic tool result content block.\n */\nexport interface AnthropicToolResultBlock {\n type: 'tool_result';\n tool_use_id: string;\n content: string;\n is_error?: boolean;\n}\n\n/**\n * Anthropic content block union type.\n */\nexport type AnthropicContentBlock =\n | AnthropicTextBlock\n | AnthropicImageBlock\n | AnthropicToolUseBlock\n | AnthropicToolResultBlock;\n\n/**\n * Anthropic message format for the Messages API.\n */\nexport interface AnthropicMessage {\n role: 'user' | 'assistant';\n content: string | AnthropicContentBlock[];\n}\n\n/**\n * Result of converting a conversation to Anthropic format.\n * System messages are extracted separately since Anthropic uses a top-level system parameter.\n */\nexport interface AnthropicConversation {\n system?: string;\n messages: AnthropicMessage[];\n}\n\n/**\n * Converts internal multi-modal content to Anthropic content blocks.\n */\nfunction toAnthropicContent(\n content: string | ReadonlyArray<MultiModalContent>,\n): string | AnthropicContentBlock[] {\n if (typeof content === 'string') {\n return content;\n }\n\n const blocks: AnthropicContentBlock[] = [];\n for (const part of content) {\n if (part.type === 'text') {\n blocks.push({ type: 'text', text: part.text ?? '' });\n } else if (part.type === 'image') {\n // Anthropic supports both URL and base64\n const url = part.url ?? '';\n if (url.startsWith('data:')) {\n // Base64 data URL\n const matches = url.match(/^data:([^;]+);base64,(.+)$/);\n if (matches && matches[1] && matches[2]) {\n blocks.push({\n type: 'image',\n source: {\n type: 'base64',\n media_type: matches[1],\n data: matches[2],\n },\n });\n }\n } else {\n // Regular URL\n blocks.push({\n type: 'image',\n source: {\n type: 'url',\n url,\n },\n });\n }\n }\n }\n\n return blocks.length === 1 && blocks[0]?.type === 'text' ? blocks[0].text : blocks;\n}\n\n/**\n * Converts an internal ToolCall to Anthropic tool_use block.\n */\nfunction toToolUseBlock(toolCall: ToolCall): AnthropicToolUseBlock {\n return {\n type: 'tool_use',\n id: toolCall.id,\n name: toolCall.name,\n input:\n typeof toolCall.arguments === 'string'\n ? JSON.parse(toolCall.arguments)\n : toolCall.arguments,\n };\n}\n\n/**\n * Converts an internal ToolResult to Anthropic tool_result block.\n */\nfunction toToolResultBlock(toolResult: ToolResult): AnthropicToolResultBlock {\n const result: AnthropicToolResultBlock = {\n type: 'tool_result',\n tool_use_id: toolResult.callId,\n content:\n typeof toolResult.content === 'string'\n ? toolResult.content\n : JSON.stringify(toolResult.content),\n };\n\n if (toolResult.outcome === 'error') {\n result.is_error = true;\n }\n\n return result;\n}\n\n/**\n * Collects system message content from a conversation.\n */\nfunction extractSystemContent(messages: ReadonlyArray<Message>): string | undefined {\n const systemMessages = messages.filter(\n (m) => (m.role === 'system' || m.role === 'developer') && !m.hidden,\n );\n\n if (systemMessages.length === 0) {\n return undefined;\n }\n\n const parts: string[] = [];\n for (const msg of systemMessages) {\n if (typeof msg.content === 'string') {\n parts.push(msg.content);\n } else {\n for (const part of msg.content) {\n if (part.type === 'text') {\n parts.push(part.text ?? '');\n }\n }\n }\n }\n\n return parts.join('\\n\\n');\n}\n\n/**\n * Converts a conversation to Anthropic Messages API format.\n * System messages are extracted to the top-level `system` field.\n * Tool calls become tool_use blocks, tool results become tool_result blocks.\n *\n * @example\n * ```ts\n * import { toAnthropicMessages } from 'conversationalist/anthropic';\n *\n * const { system, messages } = toAnthropicMessages(conversation);\n * const response = await anthropic.messages.create({\n * model: 'claude-3-opus-20240229',\n * system,\n * messages,\n * });\n * ```\n */\nexport function toAnthropicMessages(conversation: Conversation): AnthropicConversation {\n const system = extractSystemContent(conversation.messages);\n const messages: AnthropicMessage[] = [];\n\n // Track pending content blocks to merge consecutive same-role messages\n let currentRole: 'user' | 'assistant' | null = null;\n let currentBlocks: AnthropicContentBlock[] = [];\n\n const flushCurrent = () => {\n if (currentRole && currentBlocks.length > 0) {\n messages.push({\n role: currentRole,\n content:\n currentBlocks.length === 1 && currentBlocks[0]?.type === 'text'\n ? currentBlocks[0].text\n : currentBlocks,\n });\n currentBlocks = [];\n }\n currentRole = null;\n };\n\n for (const message of conversation.messages) {\n if (message.hidden) continue;\n\n // Skip system messages (already extracted)\n if (message.role === 'system' || message.role === 'developer') {\n continue;\n }\n\n // Skip snapshots\n if (message.role === 'snapshot') {\n continue;\n }\n\n let targetRole: 'user' | 'assistant';\n let blocks: AnthropicContentBlock[] = [];\n\n if (message.role === 'user') {\n targetRole = 'user';\n const content = toAnthropicContent(message.content);\n if (typeof content === 'string') {\n blocks = [{ type: 'text', text: content }];\n } else {\n blocks = content;\n }\n } else if (message.role === 'assistant') {\n targetRole = 'assistant';\n const content = toAnthropicContent(message.content);\n if (typeof content === 'string') {\n blocks = [{ type: 'text', text: content }];\n } else {\n blocks = content;\n }\n } else if (message.role === 'tool-use' && message.toolCall) {\n targetRole = 'assistant';\n blocks = [toToolUseBlock(message.toolCall)];\n } else if (message.role === 'tool-result' && message.toolResult) {\n targetRole = 'user';\n blocks = [toToolResultBlock(message.toolResult)];\n } else {\n continue;\n }\n\n // Merge with current or start new\n if (currentRole === targetRole) {\n currentBlocks.push(...blocks);\n } else {\n flushCurrent();\n currentRole = targetRole;\n currentBlocks = blocks;\n }\n }\n\n flushCurrent();\n\n const result: AnthropicConversation = { messages };\n if (system !== undefined) {\n result.system = system;\n }\n return result;\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "46DDFFB8D921BA7364756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|