ctod 0.3.6 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +8 -1
  2. package/dist/index.js +1 -1
  3. package/examples/chat-demo.ts +2 -0
  4. package/examples/chat-for-llama.cpp-demo.ts +57 -0
  5. package/examples/chat-with-json-schema-demo.ts +61 -0
  6. package/examples/plugin-demo.ts +3 -0
  7. package/examples/stream-for-llama.cpp-demo.ts +33 -0
  8. package/examples/vision-demo.ts +3 -0
  9. package/lib/broker/chat.ts +201 -95
  10. package/lib/core/translator.ts +20 -3
  11. package/lib/index.ts +23 -30
  12. package/lib/service/llama3.cpp/completion.ts +313 -0
  13. package/lib/service/llama3.cpp/index.ts +63 -0
  14. package/lib/service/openai/chat.ts +72 -3
  15. package/lib/service/openai/index.ts +8 -9
  16. package/lib/shims.d.ts +4 -0
  17. package/lib/templates.ts +13 -0
  18. package/lib/utils/validate.ts +43 -0
  19. package/package.json +5 -3
  20. package/tsconfig.json +4 -3
  21. package/types/examples/chat-demo.d.ts +2 -0
  22. package/types/examples/chat-for-llama.cpp-demo.d.ts +2 -0
  23. package/types/examples/chat-with-json-schema-demo.d.ts +2 -0
  24. package/types/examples/plugin-demo.d.ts +2 -0
  25. package/types/examples/stream-for-llama.cpp-demo.d.ts +2 -0
  26. package/types/examples/vision-demo.d.ts +2 -0
  27. package/types/lib/broker/chat.d.ts +22 -2
  28. package/types/lib/core/plugin.d.ts +2 -2
  29. package/types/lib/core/translator.d.ts +16 -2
  30. package/types/lib/index.d.ts +27 -30
  31. package/types/lib/plugins/index.d.ts +6 -6
  32. package/types/lib/plugins/limiter.d.ts +2 -1
  33. package/types/lib/service/llama3.cpp/completion.d.ts +61 -0
  34. package/types/lib/service/llama3.cpp/index.d.ts +21 -0
  35. package/types/lib/service/openai/chat.d.ts +13 -2
  36. package/types/lib/service/openai/index.d.ts +1 -3
  37. package/types/lib/templates.d.ts +5 -0
  38. package/types/lib/utils/validate.d.ts +11 -0
  39. package/lib/service/openai/completion.ts +0 -90
@@ -1,6 +1,6 @@
1
1
  import { TextParser } from '../core/parser'
2
2
  import { ChatBrokerPlugin } from '../core/plugin'
3
- import { flow, Hook, Log } from 'power-helper'
3
+ import { Event, flow, Hook, Log } from 'power-helper'
4
4
  import { Translator, TranslatorParams } from '../core/translator'
5
5
  import { ValidateCallback, ValidateCallbackOutputs } from '../utils/validate'
6
6
 
@@ -30,6 +30,10 @@ export type ChatBrokerHooks<
30
30
  send: (data: PS[K]['__receiveData']) => void
31
31
  }
32
32
  }
33
+ schema: {
34
+ input: S
35
+ output: O
36
+ }
33
37
  messages: Message[]
34
38
  setPreMessages: (messages: Message[]) => void
35
39
  changeMessages: (messages: Message[]) => void
@@ -102,6 +106,11 @@ export type ChatBrokerHooks<
102
106
  type RequestContext = {
103
107
  count: number
104
108
  isRetry: boolean
109
+ onCancel: (cb: () => void) => void
110
+ schema: {
111
+ input: any
112
+ output: any
113
+ }
105
114
  }
106
115
 
107
116
  export type Params<
@@ -136,6 +145,12 @@ export class ChatBroker<
136
145
  protected plugins = {} as PS
137
146
  protected installed = false
138
147
  protected translator: Translator<S, O>
148
+ protected event = new Event<{
149
+ cancel: {
150
+ requestId: string
151
+ }
152
+ cancelAll: any
153
+ }>()
139
154
 
140
155
  constructor(params: Params<S, O, C, P, PS>) {
141
156
  this.log = new Log(params.name ?? 'no name')
@@ -171,115 +186,206 @@ export class ChatBroker<
171
186
  }
172
187
  }
173
188
 
174
- /**
175
- * @zh 將請求發出至聊天機器人。
176
- * @en Send request to chatbot.
177
- */
189
+ async cancel(requestId?: string) {
190
+ if (requestId) {
191
+ this.event.emit('cancel', {
192
+ requestId
193
+ })
194
+ } else {
195
+ this.event.emit('cancelAll', {})
196
+ }
197
+ }
178
198
 
179
- async request<T extends Translator<S, O>>(data: T['__schemeType']): Promise<T['__outputType']> {
199
+ requestWithId<T extends Translator<S, O>>(data: T['__schemeType']): {
200
+ id: string
201
+ request: Promise<T['__outputType']>
202
+ } {
180
203
  this._install()
181
204
  let id = flow.createUuid()
182
- let output: any = null
183
- let plugins = {} as any
184
- let question = await this.translator.compile(data)
185
- let messages: Message[] = [
186
- {
187
- role: 'user',
188
- content: question.prompt
189
- }
205
+ let waitCancel = null as (() => void) | null
206
+ let isCancel = false
207
+ let isSending = false
208
+
209
+ // =================
210
+ //
211
+ // event
212
+ //
213
+
214
+ let listeners = [
215
+ this.event.on('cancel', ({ requestId }) => {
216
+ if (requestId === id) {
217
+ cancelTrigger()
218
+ }
219
+ }),
220
+ this.event.on('cancelAll', () => {
221
+ cancelTrigger()
222
+ })
190
223
  ]
191
- for (let key in this.plugins) {
192
- plugins[key] = {
193
- send: (data: any) => this.plugins[key].send({
194
- id,
195
- data
196
- })
224
+ let eventOff = () => listeners.forEach(e => e.off())
225
+ let cancelTrigger = () => {
226
+ if (isCancel === false) {
227
+ if (isSending && waitCancel) {
228
+ waitCancel()
229
+ }
230
+ isCancel = true
231
+ eventOff()
197
232
  }
198
233
  }
199
- await this.hook.notify('start', {
200
- id,
201
- data,
202
- plugins,
203
- messages,
204
- setPreMessages: ms => {
205
- messages = [
206
- ...ms,
207
- {
208
- role: 'user',
209
- content: question.prompt
210
- }
211
- ]
212
- },
213
- changeMessages: ms => {
214
- messages = ms
215
- }
216
- })
217
- await flow.asyncWhile(async ({ count, doBreak }) => {
218
- if (count >= 10) {
219
- return doBreak()
234
+ let onCancel = (cb: () => void) => {
235
+ waitCancel = cb
236
+ }
237
+
238
+ // =================
239
+ //
240
+ // main
241
+ //
242
+
243
+ let request = async() => {
244
+ let schema = this.translator.getValidate()
245
+ let output: any = null
246
+ let plugins = {} as any
247
+ let question = await this.translator.compile(data, {
248
+ schema
249
+ })
250
+ let messages: Message[] = [
251
+ {
252
+ role: 'user',
253
+ content: question.prompt
254
+ }
255
+ ]
256
+ for (let key in this.plugins) {
257
+ plugins[key] = {
258
+ send: (data: any) => this.plugins[key].send({
259
+ id,
260
+ data
261
+ })
262
+ }
220
263
  }
221
- let response = ''
222
- let parseText = ''
223
- let retryFlag = false
224
- let lastUserMessage = messages.filter(e => e.role === 'user').slice(-1)[0]?.content || ''
225
- try {
226
- await this.hook.notify('talkBefore', {
227
- id,
228
- data,
229
- messages,
230
- lastUserMessage
231
- })
232
- response = await this.params.request(messages, {
233
- count,
234
- isRetry: retryFlag
235
- })
236
- parseText = response
237
- await this.hook.notify('talkAfter', {
238
- id,
239
- data,
240
- response,
241
- messages,
242
- parseText,
243
- lastUserMessage,
244
- changeParseText: text => {
245
- parseText = text
246
- }
247
- })
248
- output = (await this.translator.parse(parseText)).output
249
- await this.hook.notify('succeeded', {
250
- id,
251
- output
252
- })
253
- await this.hook.notify('done', { id })
254
- doBreak()
255
- } catch (error: any) {
256
- // 如果解析錯誤,可以選擇是否重新解讀
257
- if (error.isParserError) {
258
- await this.hook.notify('parseFailed', {
264
+ await this.hook.notify('start', {
265
+ id,
266
+ data,
267
+ schema,
268
+ plugins,
269
+ messages,
270
+ setPreMessages: ms => {
271
+ messages = [
272
+ ...ms,
273
+ {
274
+ role: 'user',
275
+ content: question.prompt
276
+ }
277
+ ]
278
+ },
279
+ changeMessages: ms => {
280
+ messages = ms
281
+ }
282
+ })
283
+ await flow.asyncWhile(async ({ count, doBreak }) => {
284
+ if (count >= 10) {
285
+ return doBreak()
286
+ }
287
+ let response = ''
288
+ let parseText = ''
289
+ let retryFlag = false
290
+ let lastUserMessage = messages.filter(e => e.role === 'user').slice(-1)[0]?.content || ''
291
+ try {
292
+ await this.hook.notify('talkBefore', {
259
293
  id,
260
- error: error.error,
261
- count,
262
- response,
294
+ data,
263
295
  messages,
264
- lastUserMessage,
265
- parserFails: error.parserFails,
266
- retry: () => {
267
- retryFlag = true
268
- },
269
- changeMessages: ms => {
270
- messages = ms
271
- }
296
+ lastUserMessage
297
+ })
298
+ const sender = this.params.request(messages, {
299
+ count,
300
+ schema,
301
+ onCancel,
302
+ isRetry: retryFlag
272
303
  })
273
- if (retryFlag === false) {
304
+ if (isCancel) {
305
+ if (waitCancel) {
306
+ waitCancel()
307
+ }
308
+ } else {
309
+ try {
310
+ isSending = true
311
+ response = await sender
312
+ parseText = response
313
+ } finally {
314
+ isSending = false
315
+ }
316
+ }
317
+ if (isCancel === false) {
318
+ await this.hook.notify('talkAfter', {
319
+ id,
320
+ data,
321
+ response,
322
+ messages,
323
+ parseText,
324
+ lastUserMessage,
325
+ changeParseText: text => {
326
+ parseText = text
327
+ }
328
+ })
329
+ output = (await this.translator.parse(parseText)).output
330
+ await this.hook.notify('succeeded', {
331
+ id,
332
+ output
333
+ })
334
+ }
335
+ await this.hook.notify('done', { id })
336
+ doBreak()
337
+ } catch (error: any) {
338
+ // 如果解析錯誤,可以選擇是否重新解讀
339
+ if (error.isParserError) {
340
+ await this.hook.notify('parseFailed', {
341
+ id,
342
+ error: error.error,
343
+ count,
344
+ response,
345
+ messages,
346
+ lastUserMessage,
347
+ parserFails: error.parserFails,
348
+ retry: () => {
349
+ retryFlag = true
350
+ },
351
+ changeMessages: ms => {
352
+ messages = ms
353
+ }
354
+ })
355
+ if (retryFlag === false) {
356
+ await this.hook.notify('done', { id })
357
+ throw error
358
+ }
359
+ } else {
274
360
  await this.hook.notify('done', { id })
275
361
  throw error
276
362
  }
277
- } else {
278
- await this.hook.notify('done', { id })
279
- throw error
280
363
  }
364
+ })
365
+ return output
366
+ }
367
+ const send = async() => {
368
+ try {
369
+ const result = await request()
370
+ return result
371
+ } finally {
372
+ eventOff()
281
373
  }
282
- })
374
+ }
375
+ return {
376
+ id,
377
+ request: send()
378
+ }
379
+ }
380
+
381
+ /**
382
+ * @zh 將請求發出至聊天機器人。
383
+ * @en Send request to chatbot.
384
+ */
385
+
386
+ async request<T extends Translator<S, O>>(data: T['__schemeType']): Promise<T['__outputType']> {
387
+ const { request } = this.requestWithId(data)
388
+ const output = await request
283
389
  return output
284
390
  }
285
391
  }
@@ -24,7 +24,12 @@ export type TranslatorParams<
24
24
  * @zh 組合輸入資料成為提示文字。
25
25
  * @en Combine the input data into a prompt.
26
26
  */
27
- question: (data: ValidateCallbackOutputs<S>) => Promise<string>
27
+ question: (data: ValidateCallbackOutputs<S>, context: {
28
+ schema: {
29
+ input: S
30
+ output: O
31
+ }
32
+ }) => Promise<string>
28
33
  }
29
34
 
30
35
  export class Translator<
@@ -49,15 +54,27 @@ export class Translator<
49
54
  * @en Combine the input data into a prompt.
50
55
  */
51
56
 
52
- async compile(data: ValidateCallbackOutputs<S>) {
57
+ async compile(data: ValidateCallbackOutputs<S>, context: {
58
+ schema: {
59
+ input: S
60
+ output: O
61
+ }
62
+ }) {
53
63
  const scheme = validate(data, this.params.input)
54
- const prompt = await this.params.question(scheme)
64
+ const prompt = await this.params.question(scheme, context)
55
65
  return {
56
66
  scheme,
57
67
  prompt
58
68
  }
59
69
  }
60
70
 
71
+ getValidate() {
72
+ return {
73
+ input: this.params.input,
74
+ output: this.params.output
75
+ }
76
+ }
77
+
61
78
  /**
62
79
  * @zh 將文字轉換成序列化資料。
63
80
  * @en Convert text to serialized data.
package/lib/index.ts CHANGED
@@ -1,41 +1,34 @@
1
- /* eslint-disable no-redeclare */
2
-
3
- import * as _plugins from './plugins'
4
- import * as _templates from './templates'
5
- import * as _Translator from './core/translator'
6
- import { ValidateCallback } from './utils/validate'
7
- import { OpenAI as _OpenAI } from './service/openai'
8
- import { ChatGPTMessage as _ChatGPTMessage } from './service/openai/chat'
9
- import { TextParser as _TextParser } from './core/parser'
10
- import { ChatBroker as _ChatBroker } from './broker/chat'
11
- import { ChatBrokerPlugin as _ChatBrokerPlugin } from './core/plugin'
12
-
13
- export type OpenAI = _OpenAI
14
- export type TextParser = _TextParser
15
- export type Translator<S extends ValidateCallback<any>, O extends ValidateCallback<any>> = _Translator.Translator<S, O>
16
- export type TranslatorParams<S extends ValidateCallback<any>, O extends ValidateCallback<any>> = _Translator.TranslatorParams<S, O>
17
- export type ChatBroker<S extends ValidateCallback<any>, O extends ValidateCallback<any>> = _ChatBroker<S, O, any, any>
18
- export type ChatBrokerPlugin<T extends ValidateCallback<any>, R extends ValidateCallback<any>> = _ChatBrokerPlugin<T, R>
19
- export type ChatGPTMessage = _ChatGPTMessage
20
-
21
- export const OpenAI = _OpenAI
22
- export const TextParser = _TextParser
23
- export const Translator = _Translator.Translator
24
-
25
- export const ChatBroker = _ChatBroker
26
- export const ChatBrokerPlugin = _ChatBrokerPlugin
27
-
28
- export const plugins = _plugins
29
- export const templates = _templates
1
+ export * as plugins from './plugins'
2
+ export * as templates from './templates'
3
+ export { validateToJsonSchema, JsonSchemaInfo } from './utils/validate'
4
+ export { OpenAI } from './service/openai'
5
+ export { Llama3Cpp } from './service/llama3.cpp'
6
+ export { TextParser } from './core/parser'
7
+ export { ChatGPTMessage } from './service/openai/chat'
8
+ export { ChatBroker } from './broker/chat'
9
+ export { ChatBrokerPlugin } from './core/plugin'
10
+ export { Translator, TranslatorParams } from './core/translator'
11
+
12
+ import * as plugins from './plugins'
13
+ import * as templates from './templates'
14
+ import { OpenAI } from './service/openai'
15
+ import { Llama3Cpp } from './service/llama3.cpp'
16
+ import { Translator } from './core/translator'
17
+ import { TextParser } from './core/parser'
18
+ import { ChatBroker } from './broker/chat'
19
+ import { ChatBrokerPlugin } from './core/plugin'
20
+ import { validateToJsonSchema } from './utils/validate'
30
21
 
31
22
  export const ctod = {
32
23
  OpenAI,
24
+ Llama3Cpp,
33
25
  plugins,
34
26
  templates,
35
27
  ChatBroker,
36
28
  Translator,
37
29
  TextParser,
38
- ChatBrokerPlugin
30
+ ChatBrokerPlugin,
31
+ validateToJsonSchema
39
32
  }
40
33
 
41
34
  module.exports = ctod