ctod 1.1.1 → 1.1.2
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/dist/index.js +15 -16
- package/dist/index.js.map +3 -3
- package/lib/service/llama-cpp/completion.ts +83 -10
- package/lib/service/llama-cpp/index.ts +2 -1
- package/package.json +1 -1
- package/skills/ctod/SKILL.md +105 -0
|
@@ -11,6 +11,13 @@ type Message = {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
type Options = any
|
|
14
|
+
type Content = {
|
|
15
|
+
type: 'text' | 'image_url'
|
|
16
|
+
text?: string
|
|
17
|
+
image_url?: {
|
|
18
|
+
url: string
|
|
19
|
+
}
|
|
20
|
+
}[]
|
|
14
21
|
|
|
15
22
|
export type Config = {
|
|
16
23
|
baseUrl: string
|
|
@@ -201,7 +208,22 @@ export class LlamaCppCompletion {
|
|
|
201
208
|
const template = new Template(props.chat_template)
|
|
202
209
|
const prompt = template.render({
|
|
203
210
|
bos_token: props.bos_token,
|
|
204
|
-
messages: params.messages
|
|
211
|
+
messages: params.messages.map(e => {
|
|
212
|
+
const output: any = { role: e.role, content: [] as Content }
|
|
213
|
+
if (e.content) {
|
|
214
|
+
output.content.push({ type: 'text', text: this.config.autoConvertTraditionalChinese ? t2s(e.content) : e.content })
|
|
215
|
+
}
|
|
216
|
+
if (e.contents) {
|
|
217
|
+
e.contents.forEach(item => {
|
|
218
|
+
if (item.type === 'text') {
|
|
219
|
+
output.content.push({ type: 'text', text: this.config.autoConvertTraditionalChinese ? t2s(item.content) : item.content })
|
|
220
|
+
} else if (item.type === 'image') {
|
|
221
|
+
output.content.push({ type: 'image_url', image_url: { url: item.content } })
|
|
222
|
+
}
|
|
223
|
+
})
|
|
224
|
+
}
|
|
225
|
+
return output
|
|
226
|
+
})
|
|
205
227
|
}).slice(0, props.eos_token.length * -1 - 1)
|
|
206
228
|
const result = await requester.fetch({
|
|
207
229
|
path: 'completion',
|
|
@@ -244,7 +266,22 @@ export class LlamaCppCompletion {
|
|
|
244
266
|
const template = new Template(props.chat_template)
|
|
245
267
|
const prompt = template.render({
|
|
246
268
|
bos_token: props.bos_token,
|
|
247
|
-
messages: params.messages
|
|
269
|
+
messages: params.messages.map(e => {
|
|
270
|
+
const output: any = { role: e.role, content: [] as Content }
|
|
271
|
+
if (e.content) {
|
|
272
|
+
output.content.push({ type: 'text', text: this.config.autoConvertTraditionalChinese ? t2s(e.content) : e.content })
|
|
273
|
+
}
|
|
274
|
+
if (e.contents) {
|
|
275
|
+
e.contents.forEach(item => {
|
|
276
|
+
if (item.type === 'text') {
|
|
277
|
+
output.content.push({ type: 'text', text: this.config.autoConvertTraditionalChinese ? t2s(item.content) : item.content })
|
|
278
|
+
} else if (item.type === 'image') {
|
|
279
|
+
output.content.push({ type: 'image_url', image_url: { url: item.content } })
|
|
280
|
+
}
|
|
281
|
+
})
|
|
282
|
+
}
|
|
283
|
+
return output
|
|
284
|
+
})
|
|
248
285
|
}).slice(0, props.eos_token.length * -1 - 1)
|
|
249
286
|
return {
|
|
250
287
|
...(params.options || {}),
|
|
@@ -278,18 +315,30 @@ export class LlamaCppCompletion {
|
|
|
278
315
|
messages: params.messages.map(e => {
|
|
279
316
|
const output = {
|
|
280
317
|
role: e.role,
|
|
281
|
-
content:
|
|
318
|
+
content: [] as Content
|
|
282
319
|
}
|
|
283
320
|
if (e.content) {
|
|
284
|
-
output.content
|
|
321
|
+
output.content.push({
|
|
322
|
+
type: 'text',
|
|
323
|
+
text: this.config.autoConvertTraditionalChinese ? t2s(e.content) : e.content
|
|
324
|
+
})
|
|
285
325
|
}
|
|
286
326
|
if (e.contents) {
|
|
287
|
-
|
|
327
|
+
e.contents.forEach(item => {
|
|
288
328
|
if (item.type === 'text') {
|
|
289
|
-
|
|
329
|
+
output.content.push({
|
|
330
|
+
type: 'text',
|
|
331
|
+
text: this.config.autoConvertTraditionalChinese ? t2s(item.content) : item.content
|
|
332
|
+
})
|
|
333
|
+
} else if (item.type === 'image') {
|
|
334
|
+
output.content.push({
|
|
335
|
+
type: 'image_url',
|
|
336
|
+
image_url: {
|
|
337
|
+
url: item.content
|
|
338
|
+
}
|
|
339
|
+
})
|
|
290
340
|
}
|
|
291
|
-
|
|
292
|
-
}).join('\n')
|
|
341
|
+
})
|
|
293
342
|
}
|
|
294
343
|
return output
|
|
295
344
|
})
|
|
@@ -330,10 +379,34 @@ export class LlamaCppCompletion {
|
|
|
330
379
|
...(params.options || {}),
|
|
331
380
|
stream: true,
|
|
332
381
|
messages: params.messages.map(e => {
|
|
333
|
-
|
|
382
|
+
const output = {
|
|
334
383
|
role: e.role,
|
|
335
|
-
content:
|
|
384
|
+
content: [] as Content
|
|
385
|
+
}
|
|
386
|
+
if (e.content) {
|
|
387
|
+
output.content.push({
|
|
388
|
+
type: 'text',
|
|
389
|
+
text: this.config.autoConvertTraditionalChinese ? t2s(e.content) : e.content
|
|
390
|
+
})
|
|
391
|
+
}
|
|
392
|
+
if (e.contents) {
|
|
393
|
+
e.contents.forEach(item => {
|
|
394
|
+
if (item.type === 'text') {
|
|
395
|
+
output.content.push({
|
|
396
|
+
type: 'text',
|
|
397
|
+
text: this.config.autoConvertTraditionalChinese ? t2s(item.content) : item.content
|
|
398
|
+
})
|
|
399
|
+
} else if (item.type === 'image') {
|
|
400
|
+
output.content.push({
|
|
401
|
+
type: 'image_url',
|
|
402
|
+
image_url: {
|
|
403
|
+
url: item.content
|
|
404
|
+
}
|
|
405
|
+
})
|
|
406
|
+
}
|
|
407
|
+
})
|
|
336
408
|
}
|
|
409
|
+
return output
|
|
337
410
|
})
|
|
338
411
|
}
|
|
339
412
|
})
|
|
@@ -33,7 +33,8 @@ export class LlamaCppCtodService {
|
|
|
33
33
|
})
|
|
34
34
|
onCancel(cancel)
|
|
35
35
|
const { message } = await run()
|
|
36
|
-
|
|
36
|
+
const stringMessage = typeof message === 'string' ? message : JSON.stringify(message)
|
|
37
|
+
return chat.config.autoConvertTraditionalChinese ? s2t(stringMessage) : stringMessage
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ctod
|
|
3
|
+
description: Use this skill whenever writing or generating BrokerBuilder and Broker code with CtoD. Trigger this skill whenever the user asks to add or modify a Broker, or define LLM input/output structures.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# CtoD — BrokerBuilder & Broker Generation Guide
|
|
7
|
+
|
|
8
|
+
## Step 1: Create a BrokerBuilder
|
|
9
|
+
|
|
10
|
+
Define the input type and shared system prompt.
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
// This is just for reference. In best practice, the ctod instance should already be initialized
|
|
14
|
+
// in a shared place — no need to recreate it in every skill.
|
|
15
|
+
import { CtoD } from '../lib/index.js'
|
|
16
|
+
const ctod = new CtoD({ ... })
|
|
17
|
+
|
|
18
|
+
const brokerBuilder = ctod.createBrokerBuilder<{
|
|
19
|
+
question: string // input type
|
|
20
|
+
}>({
|
|
21
|
+
install: ({ attach }) => {
|
|
22
|
+
attach('start', async ({ setPreMessages }) => {
|
|
23
|
+
setPreMessages([
|
|
24
|
+
{ role: 'system', content: 'You are a helpful assistant.' }
|
|
25
|
+
])
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
})
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Step 2: Create a Broker
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
const broker = brokerBuilder.create(async ({ zod, data, setMessages }) => {
|
|
35
|
+
setMessages([
|
|
36
|
+
{
|
|
37
|
+
role: 'user',
|
|
38
|
+
contents: [{ type: 'text', content: `Question: ${data.question}` }]
|
|
39
|
+
}
|
|
40
|
+
])
|
|
41
|
+
return {
|
|
42
|
+
answer: zod.string().describe('The answer'),
|
|
43
|
+
confidence: zod.number().min(0).max(1).describe('Confidence score')
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
const result = await broker.request({ question: '...' })
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Message Formats
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
// Plain text (shorthand)
|
|
54
|
+
{ role: 'user', content: 'Hello' }
|
|
55
|
+
|
|
56
|
+
// Multi-content (with image)
|
|
57
|
+
{ role: 'user', contents: [
|
|
58
|
+
{ type: 'text', content: 'What is in this image?' },
|
|
59
|
+
{ type: 'image', content: 'data:image/png;base64,...' }
|
|
60
|
+
]}
|
|
61
|
+
|
|
62
|
+
// Multi-line text composition
|
|
63
|
+
import { paragraph } from 'ctod'
|
|
64
|
+
paragraph(['First paragraph', JSON.stringify(data), `Question: ${question}`])
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Output Schema Patterns
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
// Single value
|
|
71
|
+
{ answer: zod.string().describe('...') }
|
|
72
|
+
|
|
73
|
+
// Array
|
|
74
|
+
{
|
|
75
|
+
items: zod.array(
|
|
76
|
+
zod.object({
|
|
77
|
+
name: zod.string().describe('Name'),
|
|
78
|
+
score: zod.number().describe('Score from 0 to 1')
|
|
79
|
+
})
|
|
80
|
+
).describe('Sorted by relevance')
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Hooks (used inside BrokerBuilder install)
|
|
85
|
+
|
|
86
|
+
| Hook | Triggered when | Common params |
|
|
87
|
+
|---|---|---|
|
|
88
|
+
| `start` | Request starts | `setPreMessages`, `changeMessages`, `data`, `metadata` |
|
|
89
|
+
| `talkBefore` | Before sending | `messages`, `lastUserMessage` |
|
|
90
|
+
| `talkAfter` | After LLM responds | `changeParseText`, `parseFail` |
|
|
91
|
+
| `succeeded` | Parse succeeded | `output`, `metadata` |
|
|
92
|
+
| `parseFailed` | Parse failed | `retry`, `changeMessages` |
|
|
93
|
+
| `done` | Request ends | `id`, `metadata` |
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
// metadata shares data across hooks
|
|
97
|
+
attach('start', async ({ metadata }) => { metadata.set('key', value) })
|
|
98
|
+
attach('done', async ({ metadata }) => { metadata.get('key') })
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Notes
|
|
102
|
+
|
|
103
|
+
- `zod` is injected by ctod — **no need to import it separately**
|
|
104
|
+
- `setPreMessages` can only be used in the `start` hook
|
|
105
|
+
- `changeMessages` can be used in multiple hooks
|