ctod 1.0.7 → 1.1.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 +11 -6
- package/README_ZH.md +10 -5
- package/dist/index.js +22 -18
- package/dist/index.js.map +4 -4
- package/examples/anthropic.ts +20 -6
- package/examples/aws-bedrock.ts +11 -6
- package/examples/google.ts +11 -6
- package/examples/openai.ts +11 -6
- package/examples/x.ts +11 -6
- package/lib/broker/chat.ts +22 -2
- package/lib/ctod.ts +8 -3
- package/lib/index.ts +2 -0
- package/lib/service/anthropic/chat.ts +72 -6
- package/lib/service/anthropic/index.ts +16 -6
- package/lib/service/google/chat.ts +14 -3
- package/lib/service/google/index.ts +31 -12
- package/lib/service/llama-cpp/completion.ts +35 -7
- package/lib/service/llama-cpp/index.ts +3 -3
- package/lib/service/openai/chat.ts +47 -5
- package/lib/service/x/chat.ts +47 -5
- package/lib/utils/paragraph.ts +10 -0
- package/package.json +2 -2
- package/types/lib/broker/chat.d.ts +14 -3
- package/types/lib/ctod.d.ts +4 -2
- package/types/lib/index.d.ts +2 -0
- package/types/lib/service/anthropic/chat.d.ts +7 -2
- package/types/lib/service/google/chat.d.ts +8 -1
- package/types/lib/service/google/index.d.ts +2 -2
- package/types/lib/service/llama-cpp/completion.d.ts +6 -2
- package/types/lib/service/openai/chat.d.ts +20 -2
- package/types/lib/service/x/chat.d.ts +20 -2
- package/types/lib/utils/paragraph.d.ts +5 -0
- package/types/lib/utils/validate.d.ts +1 -1
package/examples/anthropic.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'fs'
|
|
2
2
|
import { Anthropic } from '@anthropic-ai/sdk'
|
|
3
|
-
import { CtoD, AnthropicCtodService, plugins } from '../lib/index.js'
|
|
3
|
+
import { CtoD, AnthropicCtodService, plugins, paragraph } from '../lib/index.js'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @test npx esno ./examples/anthropic.ts
|
|
@@ -38,6 +38,15 @@ const brokerBuilder = ctod.createBrokerBuilder<{
|
|
|
38
38
|
{
|
|
39
39
|
role: 'system',
|
|
40
40
|
content: 'You are now a pharmacist skilled at categorizing indexes'
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
role: 'user',
|
|
44
|
+
contents: [
|
|
45
|
+
{
|
|
46
|
+
type: 'text',
|
|
47
|
+
content: 'I have the following indexes'
|
|
48
|
+
}
|
|
49
|
+
]
|
|
41
50
|
}
|
|
42
51
|
])
|
|
43
52
|
})
|
|
@@ -49,11 +58,16 @@ const broker = brokerBuilder.create(async({ zod, data, setMessages }) => {
|
|
|
49
58
|
setMessages([
|
|
50
59
|
{
|
|
51
60
|
role: 'user',
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
contents: [
|
|
62
|
+
{
|
|
63
|
+
type: 'text',
|
|
64
|
+
content: paragraph([
|
|
65
|
+
'I have the following indexes',
|
|
66
|
+
`${JSON.stringify(indexes)}`,
|
|
67
|
+
`Please help me analyze which index "${question}" might belong to`,
|
|
68
|
+
'And sort by relevance from high to low with a score ranging from 0 to 1'
|
|
69
|
+
])
|
|
70
|
+
}
|
|
57
71
|
]
|
|
58
72
|
}
|
|
59
73
|
])
|
package/examples/aws-bedrock.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { BedrockRuntimeClient, InvokeModelCommand } from '@aws-sdk/client-bedrock-runtime'
|
|
2
|
-
import { CtoD, validateToJsonSchema, AnthropicChatDataGenerator, plugins } from '../lib/index.js'
|
|
2
|
+
import { paragraph, CtoD, validateToJsonSchema, AnthropicChatDataGenerator, plugins } from '../lib/index.js'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* @test npx esno ./examples/aws-bedrock
|
|
@@ -67,11 +67,16 @@ const broker = brokerBuilder.create(async ({ zod, data, setMessages }) => {
|
|
|
67
67
|
setMessages([
|
|
68
68
|
{
|
|
69
69
|
role: 'user',
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
contents: [
|
|
71
|
+
{
|
|
72
|
+
type: 'text',
|
|
73
|
+
content: paragraph([
|
|
74
|
+
'I have the following indexes',
|
|
75
|
+
`${JSON.stringify(indexes)}`,
|
|
76
|
+
`Please help me analyze which index "${question}" might belong to`,
|
|
77
|
+
'And sort by relevance from high to low with a score ranging from 0 to 1'
|
|
78
|
+
])
|
|
79
|
+
}
|
|
75
80
|
]
|
|
76
81
|
}
|
|
77
82
|
])
|
package/examples/google.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'fs'
|
|
2
2
|
import { GoogleGenAI } from '@google/genai'
|
|
3
|
-
import { CtoD, GoogleCtodService, plugins } from '../lib/index.js'
|
|
3
|
+
import { paragraph, CtoD, GoogleCtodService, plugins } from '../lib/index.js'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @test npx esno ./examples/google.ts
|
|
@@ -53,11 +53,16 @@ const broker = brokerBuilder.create(async({ zod, data, setMessages }) => {
|
|
|
53
53
|
setMessages([
|
|
54
54
|
{
|
|
55
55
|
role: 'user',
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
contents: [
|
|
57
|
+
{
|
|
58
|
+
type: 'text',
|
|
59
|
+
content: paragraph([
|
|
60
|
+
'I have the following indexes',
|
|
61
|
+
`${JSON.stringify(indexes)}`,
|
|
62
|
+
`Please help me analyze which index "${question}" might belong to`,
|
|
63
|
+
'And sort by relevance from high to low with a score ranging from 0 to 1'
|
|
64
|
+
])
|
|
65
|
+
}
|
|
61
66
|
]
|
|
62
67
|
}
|
|
63
68
|
])
|
package/examples/openai.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import fs from 'fs'
|
|
2
|
-
import { CtoD, OpenAICtodService, plugins } from '../lib/index.js'
|
|
2
|
+
import { CtoD, OpenAICtodService, plugins, paragraph } from '../lib/index.js'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* @test npx esno ./examples/openai.ts
|
|
@@ -45,11 +45,16 @@ const broker = brokerBuilder.create(async({ zod, data, setMessages }) => {
|
|
|
45
45
|
setMessages([
|
|
46
46
|
{
|
|
47
47
|
role: 'user',
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
contents: [
|
|
49
|
+
{
|
|
50
|
+
type: 'text',
|
|
51
|
+
content: paragraph([
|
|
52
|
+
'I have the following indexes',
|
|
53
|
+
`${JSON.stringify(indexes)}`,
|
|
54
|
+
`Please help me analyze which index "${question}" might belong to`,
|
|
55
|
+
'And sort by relevance from high to low with a score ranging from 0 to 1'
|
|
56
|
+
])
|
|
57
|
+
}
|
|
53
58
|
]
|
|
54
59
|
}
|
|
55
60
|
])
|
package/examples/x.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import fs from 'fs'
|
|
2
|
-
import { CtoD, XCtodService, plugins } from '../lib/index.js'
|
|
2
|
+
import { CtoD, XCtodService, plugins, paragraph } from '../lib/index.js'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* @test npx esno ./examples/x.ts
|
|
@@ -45,11 +45,16 @@ const broker = brokerBuilder.create(async({ zod, data, setMessages }) => {
|
|
|
45
45
|
setMessages([
|
|
46
46
|
{
|
|
47
47
|
role: 'user',
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
contents: [
|
|
49
|
+
{
|
|
50
|
+
type: 'text',
|
|
51
|
+
content: paragraph([
|
|
52
|
+
'I have the following indexes',
|
|
53
|
+
`${JSON.stringify(indexes)}`,
|
|
54
|
+
`Please help me analyze which index "${question}" might belong to`,
|
|
55
|
+
'And sort by relevance from high to low with a score ranging from 0 to 1'
|
|
56
|
+
])
|
|
57
|
+
}
|
|
53
58
|
]
|
|
54
59
|
}
|
|
55
60
|
])
|
package/lib/broker/chat.ts
CHANGED
|
@@ -5,11 +5,18 @@ import { Translator, TranslatorParams } from '../core/translator.js'
|
|
|
5
5
|
import { ValidateCallback, ValidateCallbackOutputs, validateToJsonSchema } from '../utils/validate.js'
|
|
6
6
|
import { ParserError } from '../utils/error.js'
|
|
7
7
|
import { z } from 'zod'
|
|
8
|
+
import { CtoD } from '../ctod.js'
|
|
9
|
+
|
|
10
|
+
export type PolymorphicMessage = {
|
|
11
|
+
type: 'text' | 'image'
|
|
12
|
+
content: string
|
|
13
|
+
}
|
|
8
14
|
|
|
9
15
|
export type Message = {
|
|
10
16
|
role: 'system' | 'user' | 'assistant' | (string & Record<string, unknown>)
|
|
11
17
|
name?: string
|
|
12
|
-
content
|
|
18
|
+
content?: string
|
|
19
|
+
contents?: PolymorphicMessage[]
|
|
13
20
|
}
|
|
14
21
|
|
|
15
22
|
export type ChatBrokerHooks<
|
|
@@ -38,7 +45,11 @@ export type ChatBrokerHooks<
|
|
|
38
45
|
output: O
|
|
39
46
|
}
|
|
40
47
|
messages: Message[]
|
|
41
|
-
setPreMessages: (messages: (
|
|
48
|
+
setPreMessages: (messages: (
|
|
49
|
+
Omit<Message, 'content'>
|
|
50
|
+
& { content?: string | string[] }
|
|
51
|
+
& { contents?: PolymorphicMessage[] }
|
|
52
|
+
)[]) => void
|
|
42
53
|
changeMessages: (messages: Message[]) => void
|
|
43
54
|
changeOutputSchema: (output: O) => void
|
|
44
55
|
}
|
|
@@ -213,6 +224,15 @@ export class ChatBroker<
|
|
|
213
224
|
}
|
|
214
225
|
}
|
|
215
226
|
|
|
227
|
+
cloneFrom(ctod: CtoD<any, any>) {
|
|
228
|
+
const newParams = structuredClone(this.params)
|
|
229
|
+
newParams.request = ctod.params.request
|
|
230
|
+
if (ctod.params.plugins) {
|
|
231
|
+
newParams.plugins = ctod.params.plugins
|
|
232
|
+
}
|
|
233
|
+
return new ChatBroker(newParams)
|
|
234
|
+
}
|
|
235
|
+
|
|
216
236
|
requestWithId<T extends Translator<S, O>>(data: T['__schemeType']): {
|
|
217
237
|
id: string
|
|
218
238
|
request: Promise<T['__outputType']>
|
package/lib/ctod.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ChatBroker, Message, Params as ChatBrokerParams, ChatBrokerHooks, RequestContext } from './broker/chat.js'
|
|
1
|
+
import { PolymorphicMessage, ChatBroker, Message, Params as ChatBrokerParams, ChatBrokerHooks, RequestContext } from './broker/chat.js'
|
|
2
2
|
import { ChatBrokerPlugin } from './core/plugin.js'
|
|
3
3
|
import * as z from 'zod'
|
|
4
4
|
|
|
@@ -29,7 +29,11 @@ export class CtoD<
|
|
|
29
29
|
send: (data: PS[K]['__receiveData']) => void
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
|
-
setMessages: (messages: (
|
|
32
|
+
setMessages: (messages: (
|
|
33
|
+
Omit<Message, 'content'>
|
|
34
|
+
& { content?: string | string[] }
|
|
35
|
+
& { contents?: PolymorphicMessage[] }
|
|
36
|
+
)[]) => void
|
|
33
37
|
metadata: Map<string, any>
|
|
34
38
|
}) => Promise<O>) => {
|
|
35
39
|
return new ChatBroker<
|
|
@@ -52,7 +56,8 @@ export class CtoD<
|
|
|
52
56
|
changeMessages(messages.map(e => {
|
|
53
57
|
return {
|
|
54
58
|
role: e.role,
|
|
55
|
-
content: Array.isArray(e.content) ? e.content.join('\n') : e.content
|
|
59
|
+
content: Array.isArray(e.content) ? e.content.join('\n') : e.content,
|
|
60
|
+
contents: e.contents
|
|
56
61
|
}
|
|
57
62
|
}))
|
|
58
63
|
},
|
package/lib/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ export * as chineseConverter from './utils/chinese-conv.js'
|
|
|
4
4
|
export { CtoD } from './ctod.js'
|
|
5
5
|
export { validateToJsonSchema } from './utils/validate.js'
|
|
6
6
|
export { parseJSONStream } from './utils/json.js'
|
|
7
|
+
export { paragraph } from './utils/paragraph.js'
|
|
7
8
|
export { OpenAICtodService } from './service/openai/index.js'
|
|
8
9
|
export { LlamaCppCtodService } from './service/llama-cpp/index.js'
|
|
9
10
|
export { GoogleCtodService } from './service/google/index.js'
|
|
@@ -22,3 +23,4 @@ export { Translator } from './core/translator.js'
|
|
|
22
23
|
export type { ValidateCallback } from './utils/validate.js'
|
|
23
24
|
export type { TranslatorParams } from './core/translator.js'
|
|
24
25
|
export type { ChatGPTMessage } from './service/openai/chat.js'
|
|
26
|
+
export type { PolymorphicMessage } from './broker/chat.js'
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { AnthropicCtodService } from './index.js'
|
|
2
|
+
import { flow } from 'power-helper'
|
|
3
|
+
import { PolymorphicMessage } from '../../broker/chat.js'
|
|
2
4
|
|
|
3
5
|
type AnthropicSdk = AnthropicCtodService['anthropicSdk']
|
|
4
6
|
|
|
5
7
|
export type Message = {
|
|
6
8
|
role: string
|
|
7
|
-
content
|
|
9
|
+
content?: string
|
|
10
|
+
contents?: PolymorphicMessage[]
|
|
8
11
|
}
|
|
9
12
|
|
|
10
13
|
export type Config = {
|
|
@@ -30,8 +33,66 @@ export class AnthropicChatDataGenerator {
|
|
|
30
33
|
|
|
31
34
|
private translateMessages(messages: any[]) {
|
|
32
35
|
return {
|
|
33
|
-
system:
|
|
34
|
-
|
|
36
|
+
system: flow.run(() => {
|
|
37
|
+
let systemMessage = messages.find(e => e.role === 'system')
|
|
38
|
+
let output = ''
|
|
39
|
+
if (systemMessage) {
|
|
40
|
+
if (systemMessage.content) {
|
|
41
|
+
output += systemMessage.content
|
|
42
|
+
}
|
|
43
|
+
if (systemMessage.contents) {
|
|
44
|
+
for (let contentBlock of systemMessage.contents) {
|
|
45
|
+
if (contentBlock.type === 'text') {
|
|
46
|
+
output += `\n${contentBlock.content}`
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return output
|
|
52
|
+
}),
|
|
53
|
+
messages: messages.filter(e => e.role !== 'system').map(e => {
|
|
54
|
+
const output: any[] = []
|
|
55
|
+
if (e.content) {
|
|
56
|
+
output.push({
|
|
57
|
+
type: 'text',
|
|
58
|
+
text: e.content
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
if (e.contents) {
|
|
62
|
+
for (let contentBlock of e.contents) {
|
|
63
|
+
if (contentBlock.type === 'text') {
|
|
64
|
+
output.push({
|
|
65
|
+
type: 'text',
|
|
66
|
+
text: contentBlock.content
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
if (contentBlock.type === 'image') {
|
|
70
|
+
if (contentBlock.content.startsWith('http')) {
|
|
71
|
+
output.push({
|
|
72
|
+
type: 'image',
|
|
73
|
+
source: {
|
|
74
|
+
type: 'url',
|
|
75
|
+
url: contentBlock.content
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
} else {
|
|
79
|
+
output.push({
|
|
80
|
+
type: 'image',
|
|
81
|
+
source: {
|
|
82
|
+
type: 'base64',
|
|
83
|
+
media_type: 'image/png',
|
|
84
|
+
data: contentBlock.content.split(',')[1]
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
role: e.role,
|
|
93
|
+
content: output
|
|
94
|
+
}
|
|
95
|
+
})
|
|
35
96
|
}
|
|
36
97
|
}
|
|
37
98
|
|
|
@@ -215,12 +276,13 @@ export class AnthropicChat {
|
|
|
215
276
|
messages: Message[]
|
|
216
277
|
onMessage: (_message: string) => void
|
|
217
278
|
onThinking?: (_thinking: string) => void
|
|
218
|
-
onEnd: () => void
|
|
279
|
+
onEnd: (_params: { isManualCancelled: boolean }) => void
|
|
219
280
|
onError: (_error: any) => void
|
|
220
281
|
}) {
|
|
282
|
+
let isManualCancelled = false
|
|
221
283
|
let stream: Extract<Awaited<ReturnType<typeof anthropic.messages.create>>, { controller: any }> | null = null
|
|
222
|
-
const anthropic = this.anthropic.anthropicSdk
|
|
223
284
|
const { onThinking, onMessage, onEnd, onError } = params
|
|
285
|
+
const anthropic = this.anthropic.anthropicSdk
|
|
224
286
|
const body = this.dataGenerator.createTalkStreamBody(params.messages)
|
|
225
287
|
const performStreamedChat = async () => {
|
|
226
288
|
try {
|
|
@@ -238,14 +300,18 @@ export class AnthropicChat {
|
|
|
238
300
|
}
|
|
239
301
|
}
|
|
240
302
|
}
|
|
241
|
-
onEnd(
|
|
303
|
+
onEnd({
|
|
304
|
+
isManualCancelled
|
|
305
|
+
})
|
|
242
306
|
} catch (error) {
|
|
243
307
|
onError(error)
|
|
244
308
|
}
|
|
245
309
|
}
|
|
246
310
|
performStreamedChat()
|
|
247
311
|
return {
|
|
312
|
+
isManualCancelled: () => isManualCancelled,
|
|
248
313
|
cancel: () => {
|
|
314
|
+
isManualCancelled = true
|
|
249
315
|
const int = setInterval(() => {
|
|
250
316
|
if (stream && stream.controller) {
|
|
251
317
|
stream.controller.abort()
|
|
@@ -32,12 +32,22 @@ export class AnthropicCtodService {
|
|
|
32
32
|
: e.content.map((content) => {
|
|
33
33
|
if (content.type === 'image_url') {
|
|
34
34
|
const url = content.image_url?.url || ''
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
if (url.startsWith('http')) {
|
|
36
|
+
return {
|
|
37
|
+
type: 'image',
|
|
38
|
+
source: {
|
|
39
|
+
type: url,
|
|
40
|
+
url: url
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
} else {
|
|
44
|
+
return {
|
|
45
|
+
type: 'image',
|
|
46
|
+
source: {
|
|
47
|
+
type: 'base64',
|
|
48
|
+
media_type: url.slice(5).split(';')[0],
|
|
49
|
+
data: url.split(',')[1]
|
|
50
|
+
}
|
|
41
51
|
}
|
|
42
52
|
}
|
|
43
53
|
}
|
|
@@ -14,6 +14,10 @@ type Part = {
|
|
|
14
14
|
data: string
|
|
15
15
|
mimeType: string
|
|
16
16
|
}
|
|
17
|
+
} | {
|
|
18
|
+
fileData: {
|
|
19
|
+
fileUri: string
|
|
20
|
+
}
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
export type GoogleMessage = {
|
|
@@ -127,11 +131,12 @@ export class GoogleChat {
|
|
|
127
131
|
system?: string
|
|
128
132
|
messages: GoogleMessage[]
|
|
129
133
|
onMessage: (_message: string) => void
|
|
130
|
-
onEnd: () => void
|
|
134
|
+
onEnd: (_params: { isManualCancelled: boolean }) => void
|
|
131
135
|
onThinking?: (_thinking: string) => void
|
|
132
136
|
onError: (_error: any) => void
|
|
133
137
|
}) {
|
|
134
138
|
const state = {
|
|
139
|
+
isManualCancelled: false,
|
|
135
140
|
controller: new AbortController()
|
|
136
141
|
}
|
|
137
142
|
const model = this.google.googleGenAI.models.generateContentStream({
|
|
@@ -166,10 +171,14 @@ export class GoogleChat {
|
|
|
166
171
|
}
|
|
167
172
|
}
|
|
168
173
|
}
|
|
169
|
-
params.onEnd(
|
|
174
|
+
params.onEnd({
|
|
175
|
+
isManualCancelled: state.isManualCancelled
|
|
176
|
+
})
|
|
170
177
|
} catch (error) {
|
|
171
178
|
if (state.controller.signal.aborted) {
|
|
172
|
-
params.onEnd(
|
|
179
|
+
params.onEnd({
|
|
180
|
+
isManualCancelled: state.isManualCancelled
|
|
181
|
+
})
|
|
173
182
|
} else {
|
|
174
183
|
throw error
|
|
175
184
|
}
|
|
@@ -179,7 +188,9 @@ export class GoogleChat {
|
|
|
179
188
|
params.onError(error)
|
|
180
189
|
})
|
|
181
190
|
return {
|
|
191
|
+
isManualCancelled: () => state.isManualCancelled,
|
|
182
192
|
cancel: () => {
|
|
193
|
+
state.isManualCancelled = true
|
|
183
194
|
state.controller.abort()
|
|
184
195
|
}
|
|
185
196
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { validateToJsonSchema } from '../../utils/validate.js'
|
|
2
2
|
import { GoogleMessage, GoogleChat, Config } from './chat.js'
|
|
3
3
|
import { GoogleImagesGeneration } from './images-generation.js'
|
|
4
|
+
import { OpenAIChat } from '../openai/chat.js'
|
|
4
5
|
import type { GoogleGenAI } from '@google/genai'
|
|
5
6
|
|
|
6
7
|
type GPTContent = {
|
|
7
|
-
type: 'image_url' | 'text'
|
|
8
|
+
type: 'image_url' | 'text' | 'input_text' | 'input_image'
|
|
8
9
|
text?: string
|
|
9
|
-
image_url?: {
|
|
10
|
+
image_url?: string | {
|
|
10
11
|
url: string
|
|
11
12
|
detail?: string
|
|
12
13
|
}
|
|
@@ -37,14 +38,21 @@ export class GoogleCtodService {
|
|
|
37
38
|
]
|
|
38
39
|
} else if (Array.isArray(content)) {
|
|
39
40
|
return content.map(({ type, image_url, text }): GoogleMessage['parts'][number] => {
|
|
40
|
-
if (type === 'image_url') {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
41
|
+
if (type === 'image_url' || type === 'input_image') {
|
|
42
|
+
const url = (typeof image_url === 'string' ? image_url : image_url?.url) || ''
|
|
43
|
+
if (url.startsWith('http')) {
|
|
44
|
+
return {
|
|
45
|
+
fileData: {
|
|
46
|
+
fileUri: url
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
const mimeType = url.includes('data:image/png') ? 'image/png' : 'image/jpeg'
|
|
51
|
+
return {
|
|
52
|
+
inlineData: {
|
|
53
|
+
data: url.split('base64,')[1] || '',
|
|
54
|
+
mimeType
|
|
55
|
+
}
|
|
48
56
|
}
|
|
49
57
|
}
|
|
50
58
|
} else {
|
|
@@ -59,7 +67,18 @@ export class GoogleCtodService {
|
|
|
59
67
|
let system = ''
|
|
60
68
|
const outputMessages: GoogleMessage[] = messages.map((message) => {
|
|
61
69
|
if (message.role === 'system') {
|
|
62
|
-
|
|
70
|
+
if (typeof message.content === 'string') {
|
|
71
|
+
system = message.content
|
|
72
|
+
}
|
|
73
|
+
if (Array.isArray(message.content)) {
|
|
74
|
+
system = message.content.map(part => {
|
|
75
|
+
if (part.type === 'text' || part.type === 'input_text') {
|
|
76
|
+
return part.text || ''
|
|
77
|
+
} else {
|
|
78
|
+
return ''
|
|
79
|
+
}
|
|
80
|
+
}).join('\n')
|
|
81
|
+
}
|
|
63
82
|
return {
|
|
64
83
|
role: 'user',
|
|
65
84
|
parts: []
|
|
@@ -101,7 +120,7 @@ export class GoogleCtodService {
|
|
|
101
120
|
}
|
|
102
121
|
return async (messages: any[], { schema, abortController }: any) => {
|
|
103
122
|
const config = typeof params.config === 'function' ? await params.config() : params.config
|
|
104
|
-
const context = GoogleCtodService.chatGPTMessageToGoogleChatMessage(messages)
|
|
123
|
+
const context = GoogleCtodService.chatGPTMessageToGoogleChatMessage(OpenAIChat.toApiMessages(messages) as GPTMessage[])
|
|
105
124
|
const response = await googleGenAI.models.generateContent({
|
|
106
125
|
model: params.model,
|
|
107
126
|
contents: context.messages,
|
|
@@ -2,10 +2,12 @@ import { LlamaCppCtodService } from './index.js'
|
|
|
2
2
|
import { flow, Once } from 'power-helper'
|
|
3
3
|
import { s2t, t2s } from '../../utils/chinese-conv.js'
|
|
4
4
|
import { Template } from '@huggingface/jinja'
|
|
5
|
+
import { PolymorphicMessage } from '../../broker/chat.js'
|
|
5
6
|
|
|
6
7
|
type Message = {
|
|
7
8
|
role: string
|
|
8
|
-
content
|
|
9
|
+
content?: string
|
|
10
|
+
contents?: PolymorphicMessage[]
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
type Options = any
|
|
@@ -18,12 +20,13 @@ export type Config = {
|
|
|
18
20
|
|
|
19
21
|
type Stream = {
|
|
20
22
|
onMessage: (message: string) => void
|
|
21
|
-
onEnd?: () => void
|
|
23
|
+
onEnd?: (params: { isManualCancelled: boolean }) => void
|
|
22
24
|
onWarn?: (error: any) => void
|
|
23
25
|
onError?: (error: any) => void
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
class Requester {
|
|
29
|
+
isManualCancelled = false
|
|
27
30
|
private core: LlamaCppCompletion
|
|
28
31
|
private streamAbortControllers: {
|
|
29
32
|
id: string
|
|
@@ -139,6 +142,7 @@ class Requester {
|
|
|
139
142
|
}
|
|
140
143
|
|
|
141
144
|
cancel() {
|
|
145
|
+
this.isManualCancelled = true
|
|
142
146
|
this.streamAbortControllers.forEach(e => e.controller.abort())
|
|
143
147
|
this.streamAbortControllers = []
|
|
144
148
|
}
|
|
@@ -222,7 +226,13 @@ export class LlamaCppCompletion {
|
|
|
222
226
|
const requester = new Requester(this)
|
|
223
227
|
requester.stream({
|
|
224
228
|
path: 'completion',
|
|
225
|
-
onEnd:
|
|
229
|
+
onEnd: () => {
|
|
230
|
+
if (params.onEnd) {
|
|
231
|
+
params.onEnd({
|
|
232
|
+
isManualCancelled: requester.isManualCancelled
|
|
233
|
+
})
|
|
234
|
+
}
|
|
235
|
+
},
|
|
226
236
|
onMessage: e => {
|
|
227
237
|
const message = this.config.autoConvertTraditionalChinese ? s2t(e.content) : e.content
|
|
228
238
|
params.onMessage(message)
|
|
@@ -266,10 +276,22 @@ export class LlamaCppCompletion {
|
|
|
266
276
|
...(params.options || {}),
|
|
267
277
|
response_format: params.response_format,
|
|
268
278
|
messages: params.messages.map(e => {
|
|
269
|
-
|
|
279
|
+
const output = {
|
|
270
280
|
role: e.role,
|
|
271
|
-
content:
|
|
281
|
+
content: ''
|
|
282
|
+
}
|
|
283
|
+
if (e.content) {
|
|
284
|
+
output.content = this.config.autoConvertTraditionalChinese ? t2s(e.content) : e.content
|
|
272
285
|
}
|
|
286
|
+
if (e.contents) {
|
|
287
|
+
output.content += e.contents.map(item => {
|
|
288
|
+
if (item.type === 'text') {
|
|
289
|
+
return item.content
|
|
290
|
+
}
|
|
291
|
+
return ''
|
|
292
|
+
}).join('\n')
|
|
293
|
+
}
|
|
294
|
+
return output
|
|
273
295
|
})
|
|
274
296
|
}
|
|
275
297
|
})
|
|
@@ -288,7 +310,13 @@ export class LlamaCppCompletion {
|
|
|
288
310
|
const requester = new Requester(this)
|
|
289
311
|
requester.stream({
|
|
290
312
|
path: 'v1/chat/completions',
|
|
291
|
-
onEnd:
|
|
313
|
+
onEnd: () => {
|
|
314
|
+
if (params.onEnd) {
|
|
315
|
+
params.onEnd({
|
|
316
|
+
isManualCancelled: requester.isManualCancelled
|
|
317
|
+
})
|
|
318
|
+
}
|
|
319
|
+
},
|
|
292
320
|
onMessage: e => {
|
|
293
321
|
let content = e.choices[0].delta.content
|
|
294
322
|
if (content) {
|
|
@@ -304,7 +332,7 @@ export class LlamaCppCompletion {
|
|
|
304
332
|
messages: params.messages.map(e => {
|
|
305
333
|
return {
|
|
306
334
|
role: e.role,
|
|
307
|
-
content: this.config.autoConvertTraditionalChinese ? t2s(e.content) : e.content
|
|
335
|
+
content: this.config.autoConvertTraditionalChinese ? t2s(e.content || '') : e.content
|
|
308
336
|
}
|
|
309
337
|
})
|
|
310
338
|
}
|
|
@@ -12,13 +12,13 @@ export class LlamaCppCtodService {
|
|
|
12
12
|
talkOptions?: any
|
|
13
13
|
}) {
|
|
14
14
|
return async(messages: any[], { schema, onCancel }: any) => {
|
|
15
|
-
const
|
|
16
|
-
const chat =
|
|
15
|
+
const llamaCpp = new LlamaCppCtodService()
|
|
16
|
+
const chat = llamaCpp.createCompletion()
|
|
17
17
|
const config = typeof params.config === 'function' ? await params.config() : params.config
|
|
18
18
|
chat.setConfig(config)
|
|
19
19
|
let formatSchema = validateToJsonSchema(schema.output)
|
|
20
20
|
if (params.axios) {
|
|
21
|
-
|
|
21
|
+
llamaCpp.setAxios(params.axios)
|
|
22
22
|
}
|
|
23
23
|
if (chat.config.autoConvertTraditionalChinese) {
|
|
24
24
|
formatSchema = JSON.parse(t2s(JSON.stringify(formatSchema)))
|