ctod 0.6.3 → 0.7.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/.api-key +1 -0
- package/.nyc_output/42919e68-b472-4a5d-b2d3-5d5153f28467.json +1 -0
- package/.nyc_output/processinfo/42919e68-b472-4a5d-b2d3-5d5153f28467.json +1 -0
- package/.nyc_output/processinfo/index.json +1 -0
- package/.output/.output/stores//344/270/200/345/240/264/346/234/237/345/276/205/345/267/262/344/271/205/347/232/204/345/222/214/350/247/243/cover-0.png +1 -0
- package/.output/.output/stores//344/270/200/345/240/264/346/234/237/345/276/205/345/267/262/344/271/205/347/232/204/345/222/214/350/247/243/cover-1.png +1 -0
- package/.output/.output/stores//344/270/200/345/240/264/346/234/237/345/276/205/345/267/262/344/271/205/347/232/204/345/222/214/350/247/243/story-config.json +4 -0
- package/.output/.output/stores//344/270/200/345/240/264/346/234/237/345/276/205/345/267/262/344/271/205/347/232/204/345/222/214/350/247/243/story.json +22 -0
- package/.output/.output/stores//345/276/236/351/273/221/346/232/227/350/265/260/345/220/221/345/205/211/346/230/216/cover-0.png +1 -0
- package/.output/.output/stores//345/276/236/351/273/221/346/232/227/350/265/260/345/220/221/345/205/211/346/230/216/cover-1.png +1 -0
- package/.output/.output/stores//345/276/236/351/273/221/346/232/227/350/265/260/345/220/221/345/205/211/346/230/216/story-config.json +4 -0
- package/.output/.output/stores//345/276/236/351/273/221/346/232/227/350/265/260/345/220/221/345/205/211/346/230/216/story.json +24 -0
- package/.output/.output/stores//347/240/264/347/242/216/347/232/204/345/271/273/350/261/241/cover-0.png +1 -0
- package/.output/.output/stores//347/240/264/347/242/216/347/232/204/345/271/273/350/261/241/cover-1.png +1 -0
- package/.output/.output/stores//347/240/264/347/242/216/347/232/204/345/271/273/350/261/241/story-config.json +4 -0
- package/.output/.output/stores//347/240/264/347/242/216/347/232/204/345/271/273/350/261/241/story.json +24 -0
- package/.output/.output/stores//350/227/235/350/241/223/345/256/266/347/232/204/351/235/210/346/204/237/cover-0.png +1 -0
- package/.output/.output/stores//350/227/235/350/241/223/345/256/266/347/232/204/351/235/210/346/204/237/cover-1.png +1 -0
- package/.output/.output/stores//350/227/235/350/241/223/345/256/266/347/232/204/351/235/210/346/204/237/story-config.json +4 -0
- package/.output/.output/stores//350/227/235/350/241/223/345/256/266/347/232/204/351/235/210/346/204/237/story.json +28 -0
- package/.output/.output/stores//350/250/230/346/206/266/345/225/206/344/272/272/cover-0.png +1 -0
- package/.output/.output/stores//350/250/230/346/206/266/345/225/206/344/272/272/cover-1.png +1 -0
- package/.output/.output/stores//350/250/230/346/206/266/345/225/206/344/272/272/story-config.json +4 -0
- package/.output/.output/stores//350/250/230/346/206/266/345/225/206/344/272/272/story.json +18 -0
- package/.output/.output/stores//350/250/255/350/250/210/350/210/207/351/226/213/347/231/274/347/232/204/346/214/221/346/210/260/content.json +22 -0
- package/.output/.output/talks//344/273/245/347/254/221/350/251/261/347/202/272/345/237/272/347/244/216/347/232/204/351/235/242/350/251/246/content.json +30 -0
- package/.output/.output/talks//344/273/245/347/254/221/350/251/261/347/202/272/345/237/272/347/244/216/347/232/204/351/235/242/350/251/246/cover-1684055229695.png +0 -0
- package/.output/.output/talks//345/234/250/346/224/277/346/262/273/345/200/231/351/201/270/344/272/272/350/276/257/350/253/226/344/270/255/347/232/204/350/252/252/346/234/215/346/200/247/346/272/235/351/200/232/content.json +30 -0
- package/.output/.output/talks//345/234/250/346/224/277/346/262/273/345/200/231/351/201/270/344/272/272/350/276/257/350/253/226/344/270/255/347/232/204/350/252/252/346/234/215/346/200/247/346/272/235/351/200/232/cover-1684056611678.png +0 -0
- package/.output/.output/talks//346/224/276/351/254/206/347/232/204/345/200/231/351/201/270/344/272/272/351/200/262/350/241/214/346/224/277/346/262/273/350/276/257/350/253/226/content.json +36 -0
- package/.output/.output/talks//346/224/276/351/254/206/347/232/204/345/200/231/351/201/270/344/272/272/351/200/262/350/241/214/346/224/277/346/262/273/350/276/257/350/253/226/cover-1684055140609.png +0 -0
- package/.output/.output/talks//346/224/277/346/262/273/347/254/221/350/251/261/content.json +30 -0
- package/.output/.output/talks//346/224/277/346/262/273/347/254/221/350/251/261/cover-1684056246465.png +0 -0
- package/.output/.output/talks//350/251/274/350/253/247/345/256/266/351/225/267/346/225/231/345/270/253/346/234/203/350/255/260/content.json +26 -0
- package/.output/.output/talks//350/251/274/350/253/247/345/256/266/351/225/267/346/225/231/345/270/253/346/234/203/350/255/260/cover-1685785935121.png +0 -0
- package/.output/.output/talks//350/262/241/345/213/231/351/241/247/345/225/217/350/253/256/350/251/242/content.json +26 -0
- package/.output/.output/talks//350/262/241/345/213/231/351/241/247/345/225/217/350/253/256/350/251/242/cover-1685785115833.png +0 -0
- package/.output/.output/talks//351/206/253/347/224/237/345/222/214/347/227/205/344/272/272/350/250/216/350/253/226/346/202/262/345/202/267/content.json +32 -0
- package/.output/.output/talks//351/206/253/347/224/237/345/222/214/347/227/205/344/272/272/350/250/216/350/253/226/346/202/262/345/202/267/cover-1684055075942.png +0 -0
- package/README.md +87 -84
- package/dist/index.js +1 -1
- package/package.json +4 -4
- package/examples/chat-demo.ts +0 -89
- package/examples/chat-for-llama.cpp-demo.ts +0 -57
- package/examples/plugin-demo.ts +0 -110
- package/examples/stream-for-llama.cpp-demo.ts +0 -33
- package/examples/vision-demo-ex.png +0 -0
- package/examples/vision-demo.png +0 -0
- package/examples/vision-demo.ts +0 -168
- package/lib/broker/chat.ts +0 -406
- package/lib/core/parser.ts +0 -62
- package/lib/core/plugin.ts +0 -46
- package/lib/core/translator.ts +0 -111
- package/lib/index.ts +0 -38
- package/lib/plugins/index.ts +0 -38
- package/lib/plugins/limiter.ts +0 -103
- package/lib/plugins/print-log.ts +0 -35
- package/lib/plugins/retry.ts +0 -25
- package/lib/plugins/role.ts +0 -28
- package/lib/service/llama3.cpp/completion.ts +0 -313
- package/lib/service/llama3.cpp/index.ts +0 -53
- package/lib/service/openai/chat.ts +0 -244
- package/lib/service/openai/images-generation.ts +0 -64
- package/lib/service/openai/index.ts +0 -97
- package/lib/service/openai/vision.ts +0 -111
- package/lib/shims.d.ts +0 -4
- package/lib/templates.ts +0 -71
- package/lib/types.ts +0 -4
- package/lib/utils/error.ts +0 -14
- package/lib/utils/validate.ts +0 -64
- package/logo.ai +2 -1141
- package/logo.png +0 -0
- package/types/examples/chat-demo.d.ts +0 -2
- package/types/examples/chat-for-llama.cpp-demo.d.ts +0 -2
- package/types/examples/chat-with-json-schema-demo.d.ts +0 -2
- package/types/examples/plugin-demo.d.ts +0 -2
- package/types/examples/stream-for-llama.cpp-demo.d.ts +0 -2
- package/types/examples/vision-demo.d.ts +0 -2
- package/types/lib/broker/chat.d.ts +0 -142
- package/types/lib/core/parser.d.ts +0 -32
- package/types/lib/core/plugin.d.ts +0 -34
- package/types/lib/core/translator.d.ts +0 -68
- package/types/lib/index.d.ts +0 -31
- package/types/lib/plugins/index.d.ts +0 -47
- package/types/lib/plugins/limiter.d.ts +0 -36
- package/types/lib/plugins/print-log.d.ts +0 -5
- package/types/lib/plugins/retry.d.ts +0 -6
- package/types/lib/plugins/role.d.ts +0 -5
- package/types/lib/service/llama3.cpp/completion.d.ts +0 -61
- package/types/lib/service/llama3.cpp/index.d.ts +0 -19
- package/types/lib/service/openai/chat.d.ts +0 -110
- package/types/lib/service/openai/completion.d.ts +0 -59
- package/types/lib/service/openai/images-generation.d.ts +0 -35
- package/types/lib/service/openai/index.d.ts +0 -29
- package/types/lib/service/openai/vision.d.ts +0 -74
- package/types/lib/templates.d.ts +0 -20
- package/types/lib/types.d.ts +0 -1
- package/types/lib/utils/error.d.ts +0 -11
- package/types/lib/utils/validate.d.ts +0 -16
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
import { json } from 'power-helper'
|
|
2
|
-
import { OpenAI } from './index'
|
|
3
|
-
import { PromiseResponseType } from '../../types'
|
|
4
|
-
|
|
5
|
-
export type ChatGPTMessage = {
|
|
6
|
-
role: 'system' | 'user' | 'assistant'
|
|
7
|
-
name?: string
|
|
8
|
-
content: string
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
type ApiResponse = {
|
|
12
|
-
id: string
|
|
13
|
-
object: string
|
|
14
|
-
created: number
|
|
15
|
-
choices: Array<{
|
|
16
|
-
index: number
|
|
17
|
-
finish_reason: string
|
|
18
|
-
message: {
|
|
19
|
-
role: 'system' | 'user' | 'assistant'
|
|
20
|
-
name?: string
|
|
21
|
-
content: string
|
|
22
|
-
}
|
|
23
|
-
}>
|
|
24
|
-
usage: {
|
|
25
|
-
prompt_tokens: number
|
|
26
|
-
completion_tokens: number
|
|
27
|
-
total_tokens: number
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export type Config = {
|
|
32
|
-
/**
|
|
33
|
-
* @zh 一次回應數量
|
|
34
|
-
* @en How many chat completion choices to generate for each input message.
|
|
35
|
-
*/
|
|
36
|
-
n: number
|
|
37
|
-
/**
|
|
38
|
-
* @zh 選擇運行的模型,16k意味著能處理長度為 16,384 的文本,32k意味著能處理長度為 32768 的文本。
|
|
39
|
-
* @en How many chat completion choices to generate for each input message.
|
|
40
|
-
*/
|
|
41
|
-
model: 'gpt-4' | 'gpt-3.5-turbo' | 'gpt-4-turbo' | 'gpt-4o' | 'gpt-4o-mini' | 'o1-preview' | 'o1' | 'o1-mini'
|
|
42
|
-
/**
|
|
43
|
-
* @zh 冒險指數,數值由 0 ~ 2 之間,越低回應越穩定。
|
|
44
|
-
* @en What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.
|
|
45
|
-
*/
|
|
46
|
-
temperature: number
|
|
47
|
-
/**
|
|
48
|
-
* @zh 是否強制要回傳 JSON 格式的資料
|
|
49
|
-
* @en Whether to force the return of JSON format data
|
|
50
|
-
*/
|
|
51
|
-
forceJsonFormat: boolean
|
|
52
|
-
/**
|
|
53
|
-
* @zh 每次對話最多產生幾個 tokens。
|
|
54
|
-
* @en How many tokens to complete to.
|
|
55
|
-
*/
|
|
56
|
-
maxTokens?: number
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export class OpenAIChat {
|
|
60
|
-
openai: OpenAI
|
|
61
|
-
config: Config = {
|
|
62
|
-
n: 1,
|
|
63
|
-
model: 'gpt-4o',
|
|
64
|
-
temperature: 1,
|
|
65
|
-
maxTokens: undefined,
|
|
66
|
-
forceJsonFormat: true
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
constructor(openai: OpenAI) {
|
|
70
|
-
this.openai = openai
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* @zh 改變對話的一些設定
|
|
75
|
-
* @en Change some settings of the conversation
|
|
76
|
-
*/
|
|
77
|
-
|
|
78
|
-
setConfig(options: Partial<Config>) {
|
|
79
|
-
Object.assign(this.config, options)
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* @zh 檢視內容是否符合 OpenAI 的審查
|
|
84
|
-
* @en View content for OpenAI moderation
|
|
85
|
-
*/
|
|
86
|
-
|
|
87
|
-
async moderations(input: string) {
|
|
88
|
-
const result = await this.openai._axios.post<any>('https://api.openai.com/v1/moderations', {
|
|
89
|
-
input: input
|
|
90
|
-
}, {
|
|
91
|
-
headers: {
|
|
92
|
-
'Content-Type': 'application/json',
|
|
93
|
-
'Authorization': `Bearer ${this.openai._apiKey}`
|
|
94
|
-
}
|
|
95
|
-
})
|
|
96
|
-
return {
|
|
97
|
-
isSafe: result.data.results?.[0]?.flagged === false,
|
|
98
|
-
result: result.data
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* @zh 進行對話
|
|
104
|
-
* @en Talk to the AI
|
|
105
|
-
*/
|
|
106
|
-
|
|
107
|
-
async talk(messages: ChatGPTMessage[] = [], options?: {
|
|
108
|
-
/** 要 forceJsonFormat 為 true 才會生效 */
|
|
109
|
-
jsonSchema?: any
|
|
110
|
-
abortController?: AbortController
|
|
111
|
-
}) {
|
|
112
|
-
const newMessages = json.jpjs(messages)
|
|
113
|
-
const isSupportJson = [
|
|
114
|
-
'gpt-4-turbo-preview',
|
|
115
|
-
'gpt-4-turbo',
|
|
116
|
-
'gpt-4o',
|
|
117
|
-
'gpt-4o-mini',
|
|
118
|
-
'gpt-3.5-turbo-1106'
|
|
119
|
-
].includes(this.config.model)
|
|
120
|
-
let response_format: any = undefined
|
|
121
|
-
if (isSupportJson && this.config.forceJsonFormat) {
|
|
122
|
-
response_format = {
|
|
123
|
-
type: 'json_object'
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
if (isSupportJson && this.config.forceJsonFormat && options?.jsonSchema) {
|
|
127
|
-
response_format = {
|
|
128
|
-
type: 'json_schema',
|
|
129
|
-
json_schema: options.jsonSchema
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
const result = await this.openai._axios.post<ApiResponse>('https://api.openai.com/v1/chat/completions', {
|
|
133
|
-
model: this.config.model,
|
|
134
|
-
n: this.config.n,
|
|
135
|
-
messages: newMessages,
|
|
136
|
-
response_format,
|
|
137
|
-
temperature: this.config.temperature
|
|
138
|
-
}, {
|
|
139
|
-
signal: options?.abortController?.signal,
|
|
140
|
-
headers: {
|
|
141
|
-
'Content-Type': 'application/json',
|
|
142
|
-
'Authorization': `Bearer ${this.openai._apiKey}`
|
|
143
|
-
}
|
|
144
|
-
})
|
|
145
|
-
const choices = result.data.choices || []
|
|
146
|
-
const message = choices[0]?.message || {
|
|
147
|
-
role: 'assistant',
|
|
148
|
-
content: ''
|
|
149
|
-
}
|
|
150
|
-
newMessages.push(message)
|
|
151
|
-
return {
|
|
152
|
-
id: result?.data.id as string,
|
|
153
|
-
text: message.content as string,
|
|
154
|
-
newMessages,
|
|
155
|
-
isDone: choices[0]?.finish_reason === 'stop',
|
|
156
|
-
apiReseponse: result.data
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
talkStream(params: {
|
|
161
|
-
messages: any[]
|
|
162
|
-
onMessage: (message: string) => void
|
|
163
|
-
onEnd: () => void
|
|
164
|
-
onWarn: (warn: any) => void
|
|
165
|
-
onError: (error: any) => void
|
|
166
|
-
}) {
|
|
167
|
-
const controller = new AbortController()
|
|
168
|
-
fetch('https://api.openai.com/v1/chat/completions', {
|
|
169
|
-
method: 'POST',
|
|
170
|
-
headers: {
|
|
171
|
-
'Content-Type': 'application/json',
|
|
172
|
-
Authorization: `Bearer ${this.openai._apiKey}`
|
|
173
|
-
},
|
|
174
|
-
body: JSON.stringify({
|
|
175
|
-
model: this.config.model,
|
|
176
|
-
stream: true,
|
|
177
|
-
messages: params.messages
|
|
178
|
-
}),
|
|
179
|
-
signal: controller.signal
|
|
180
|
-
}).then(async response => {
|
|
181
|
-
// eslint-disable-next-line no-undef
|
|
182
|
-
const reader = response.body?.pipeThrough(new TextDecoderStream()).getReader()
|
|
183
|
-
if (!reader) {
|
|
184
|
-
throw new Error('Can not get reader')
|
|
185
|
-
}
|
|
186
|
-
// eslint-disable-next-line no-constant-condition
|
|
187
|
-
while (true) {
|
|
188
|
-
const { value, done } = await reader.read()
|
|
189
|
-
if (done) {
|
|
190
|
-
break
|
|
191
|
-
}
|
|
192
|
-
const items = value.split('\n')
|
|
193
|
-
for (let item of items) {
|
|
194
|
-
if (item.length === 0) {
|
|
195
|
-
continue
|
|
196
|
-
}
|
|
197
|
-
if (item.startsWith(':')) {
|
|
198
|
-
continue
|
|
199
|
-
}
|
|
200
|
-
if (item === 'data: [DONE]') {
|
|
201
|
-
params.onEnd()
|
|
202
|
-
break
|
|
203
|
-
}
|
|
204
|
-
try {
|
|
205
|
-
const result = JSON.parse(item.substring(6))
|
|
206
|
-
const content = result.choices[0].delta.content
|
|
207
|
-
params.onMessage(content)
|
|
208
|
-
} catch (error) {
|
|
209
|
-
params.onWarn(error)
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}).catch(error => {
|
|
214
|
-
if (error.name === 'AbortError') {
|
|
215
|
-
params.onEnd()
|
|
216
|
-
} else {
|
|
217
|
-
params.onError(error)
|
|
218
|
-
}
|
|
219
|
-
})
|
|
220
|
-
return {
|
|
221
|
-
cancel: () => controller.abort()
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* @zh 開啟持續性對話
|
|
227
|
-
*/
|
|
228
|
-
|
|
229
|
-
async keepTalk(prompt: string | string[], oldMessages: ChatGPTMessage[] = []) {
|
|
230
|
-
const result = await this.talk([
|
|
231
|
-
...oldMessages,
|
|
232
|
-
{
|
|
233
|
-
role: 'user',
|
|
234
|
-
content: Array.isArray(prompt) ? prompt.join('\n') : prompt
|
|
235
|
-
}
|
|
236
|
-
])
|
|
237
|
-
return {
|
|
238
|
-
result,
|
|
239
|
-
nextTalk: (prompt: string | string[]) => this.keepTalk(prompt, result.newMessages)
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
export type OpenAIChatTalkResponse = PromiseResponseType<OpenAIChat['talk']>
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import { OpenAI } from './index'
|
|
2
|
-
|
|
3
|
-
type ApiResponse = {
|
|
4
|
-
created: string
|
|
5
|
-
data: {
|
|
6
|
-
b64_json: string
|
|
7
|
-
}[]
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
type Config = {
|
|
11
|
-
/**
|
|
12
|
-
* @zh 模型,支援 dall-e-2 和 dall-e-3
|
|
13
|
-
* @en Model, support dall-e-2 and dall-e-3
|
|
14
|
-
*/
|
|
15
|
-
model: 'dall-e-2' | 'dall-e-3'
|
|
16
|
-
/**
|
|
17
|
-
* @zh 解析度,例如 1024x1024
|
|
18
|
-
* @en Resolution, for example 1024x1024
|
|
19
|
-
*/
|
|
20
|
-
size: `${number}x${number}`
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export class OpenAIImagesGeneration {
|
|
24
|
-
private openai: OpenAI
|
|
25
|
-
private config: Config = {
|
|
26
|
-
model: 'dall-e-2',
|
|
27
|
-
size: '1024x1024'
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
constructor(openai: OpenAI) {
|
|
31
|
-
this.openai = openai
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* @zh 改變對話的一些設定
|
|
36
|
-
* @en Change some settings of the conversation
|
|
37
|
-
*/
|
|
38
|
-
|
|
39
|
-
setConfig(options: Partial<Config>) {
|
|
40
|
-
Object.assign(this.config, options)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* @zh 產生圖片
|
|
45
|
-
* @en Generate image
|
|
46
|
-
*/
|
|
47
|
-
|
|
48
|
-
async create(prompt: string) {
|
|
49
|
-
const result = await this.openai._axios.post<ApiResponse>('https://api.openai.com/v1/images/generations', {
|
|
50
|
-
prompt,
|
|
51
|
-
n: 1,
|
|
52
|
-
size: this.config.size,
|
|
53
|
-
model: this.config.model,
|
|
54
|
-
response_format: 'b64_json'
|
|
55
|
-
}, {
|
|
56
|
-
timeout: 1000 * 60 * 5,
|
|
57
|
-
headers: {
|
|
58
|
-
'Content-Type': 'application/json',
|
|
59
|
-
'Authorization': `Bearer ${this.openai._apiKey}`
|
|
60
|
-
}
|
|
61
|
-
})
|
|
62
|
-
return result.data
|
|
63
|
-
}
|
|
64
|
-
}
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import axios from 'axios'
|
|
2
|
-
import { OpenAIVision } from './vision'
|
|
3
|
-
import { OpenAIChat, Config } from './chat'
|
|
4
|
-
import { OpenAIImagesGeneration } from './images-generation'
|
|
5
|
-
import { validateToJsonSchema } from '../../utils/validate'
|
|
6
|
-
|
|
7
|
-
export class OpenAI {
|
|
8
|
-
_axios = axios.create()
|
|
9
|
-
_apiKey = ''
|
|
10
|
-
|
|
11
|
-
static createChatRequest(
|
|
12
|
-
apiKey: string | (() => Promise<string>),
|
|
13
|
-
config: Partial<Config> | (() => Promise<Partial<Config>>) = {},
|
|
14
|
-
options?: {
|
|
15
|
-
axios?: any
|
|
16
|
-
}
|
|
17
|
-
) {
|
|
18
|
-
return async(messages: any[], { onCancel }: any) => {
|
|
19
|
-
const openai = new OpenAI(typeof apiKey === 'string' ? apiKey : await apiKey())
|
|
20
|
-
const chat = openai.createChat()
|
|
21
|
-
const abortController = new AbortController()
|
|
22
|
-
if (options && options.axios) {
|
|
23
|
-
openai.setAxios(options.axios)
|
|
24
|
-
}
|
|
25
|
-
chat.setConfig(typeof config === 'function' ? await config() : config)
|
|
26
|
-
onCancel(() => abortController.abort())
|
|
27
|
-
const { text } = await chat.talk(messages, {
|
|
28
|
-
abortController
|
|
29
|
-
})
|
|
30
|
-
return text
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
static createChatRequestWithJsonSchema(params:{
|
|
35
|
-
axios?: any,
|
|
36
|
-
apiKey: string | (() => Promise<string>),
|
|
37
|
-
config?: Partial<Pick<Config, 'model' | 'temperature'>> | (() => Promise<Partial<Pick<Config, 'model' | 'temperature'>>>)
|
|
38
|
-
}) {
|
|
39
|
-
return async(messages: any[], { schema, onCancel }: any) => {
|
|
40
|
-
const openai = new OpenAI(typeof params.apiKey === 'string' ? params.apiKey : await params.apiKey())
|
|
41
|
-
const chat = openai.createChat()
|
|
42
|
-
const abortController = new AbortController()
|
|
43
|
-
if (params.config) {
|
|
44
|
-
chat.setConfig(typeof params.config === 'function' ? await params.config() : params.config)
|
|
45
|
-
}
|
|
46
|
-
if (params.axios) {
|
|
47
|
-
openai.setAxios(params.axios)
|
|
48
|
-
}
|
|
49
|
-
onCancel(() => abortController.abort())
|
|
50
|
-
const jsonSchema = validateToJsonSchema(schema.output)
|
|
51
|
-
const { text } = await chat.talk(messages, {
|
|
52
|
-
abortController,
|
|
53
|
-
jsonSchema: {
|
|
54
|
-
name: 'data',
|
|
55
|
-
strict: true,
|
|
56
|
-
schema: jsonSchema
|
|
57
|
-
}
|
|
58
|
-
})
|
|
59
|
-
return text
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
constructor(apiKey = '') {
|
|
65
|
-
this._apiKey = apiKey
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* @zh 如果你有需要特別設定 axios,請使用這方法。
|
|
70
|
-
* @en If you need to set axios, please use this method.
|
|
71
|
-
*/
|
|
72
|
-
|
|
73
|
-
setAxios(axios: any) {
|
|
74
|
-
this._axios = axios
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* @zh 設定 api key。
|
|
79
|
-
* @en Set api key.
|
|
80
|
-
*/
|
|
81
|
-
|
|
82
|
-
setConfiguration(apiKey: string) {
|
|
83
|
-
this._apiKey = apiKey
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
createChat() {
|
|
87
|
-
return new OpenAIChat(this)
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
createVision() {
|
|
91
|
-
return new OpenAIVision(this)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
createImagesGeneration() {
|
|
95
|
-
return new OpenAIImagesGeneration(this)
|
|
96
|
-
}
|
|
97
|
-
}
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import { OpenAI } from './index'
|
|
2
|
-
import { PromiseResponseType } from '../../types'
|
|
3
|
-
|
|
4
|
-
type ImageContent = {
|
|
5
|
-
type: 'image_url' | 'text'
|
|
6
|
-
text?: string
|
|
7
|
-
image_url?: {
|
|
8
|
-
url: string
|
|
9
|
-
detail?: string
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
type VisionMessage = {
|
|
14
|
-
role: 'system' | 'user' | 'assistant'
|
|
15
|
-
name?: string
|
|
16
|
-
content: string | ImageContent[]
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
type ApiResponse = {
|
|
20
|
-
id: string
|
|
21
|
-
object: string
|
|
22
|
-
created: number
|
|
23
|
-
model: string
|
|
24
|
-
usage: {
|
|
25
|
-
prompt_tokens: number
|
|
26
|
-
completion_tokens: number
|
|
27
|
-
total_tokens: number
|
|
28
|
-
}
|
|
29
|
-
choices: Array<{
|
|
30
|
-
message: {
|
|
31
|
-
role: string
|
|
32
|
-
content: string
|
|
33
|
-
}
|
|
34
|
-
finish_details: {
|
|
35
|
-
type: string
|
|
36
|
-
}
|
|
37
|
-
index: number
|
|
38
|
-
}>
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export type Config = {
|
|
42
|
-
/**
|
|
43
|
-
* @zh 選擇運行的模型。
|
|
44
|
-
* @en How many chat completion choices to generate for each input message.
|
|
45
|
-
*/
|
|
46
|
-
model: 'gpt-4-vision-preview'
|
|
47
|
-
/**
|
|
48
|
-
* @zh 冒險指數,數值由 0 ~ 2 之間。
|
|
49
|
-
* @en What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.
|
|
50
|
-
*/
|
|
51
|
-
temperature: number
|
|
52
|
-
/**
|
|
53
|
-
* @zh 每次對話最多產生幾個 tokens。
|
|
54
|
-
* @en How many tokens to complete to.
|
|
55
|
-
*/
|
|
56
|
-
maxTokens?: number
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export class OpenAIVision {
|
|
60
|
-
openai: OpenAI
|
|
61
|
-
config: Config = {
|
|
62
|
-
model: 'gpt-4-vision-preview',
|
|
63
|
-
maxTokens: undefined,
|
|
64
|
-
temperature: 1
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
constructor(openai: OpenAI) {
|
|
68
|
-
this.openai = openai
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* @zh 改變對話的一些設定
|
|
73
|
-
* @en Change some settings of the conversation
|
|
74
|
-
*/
|
|
75
|
-
|
|
76
|
-
setConfig(options: Partial<Config>) {
|
|
77
|
-
Object.assign(this.config, options)
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* @zh 辨識圖片
|
|
82
|
-
* @en Recognize images
|
|
83
|
-
*/
|
|
84
|
-
|
|
85
|
-
async view(messages: VisionMessage[]) {
|
|
86
|
-
const result = await this.openai._axios.post<ApiResponse>('https://api.openai.com/v1/chat/completions', {
|
|
87
|
-
model: this.config.model,
|
|
88
|
-
n: 1,
|
|
89
|
-
messages,
|
|
90
|
-
max_tokens: this.config.maxTokens,
|
|
91
|
-
temperature: this.config.temperature
|
|
92
|
-
}, {
|
|
93
|
-
headers: {
|
|
94
|
-
'Content-Type': 'application/json',
|
|
95
|
-
'Authorization': `Bearer ${this.openai._apiKey}`
|
|
96
|
-
}
|
|
97
|
-
})
|
|
98
|
-
const choices = result.data.choices || []
|
|
99
|
-
const message = choices[0]?.message || {
|
|
100
|
-
role: 'assistant',
|
|
101
|
-
content: ''
|
|
102
|
-
}
|
|
103
|
-
return {
|
|
104
|
-
id: result?.data.id as string,
|
|
105
|
-
text: message.content as string,
|
|
106
|
-
apiReseponse: result.data
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export type OpenAIChatVisionResponse = PromiseResponseType<OpenAIVision['view']>
|
package/lib/shims.d.ts
DELETED
package/lib/templates.ts
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import Handlebars from 'handlebars'
|
|
2
|
-
import { record } from 'power-helper'
|
|
3
|
-
|
|
4
|
-
type JsonResponseFormat = {
|
|
5
|
-
desc: string
|
|
6
|
-
example: any
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @zh 協助使用者將格式需求轉成論敘語句。
|
|
11
|
-
* @en Assist the user in converting the formatting requirements into declarative sentences.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
export const requireJsonResponse = (question: string | string[], format: Record<string, JsonResponseFormat>) => {
|
|
15
|
-
return [
|
|
16
|
-
...(Array.isArray(question) ? question : [question]),
|
|
17
|
-
'Please respond using the following JSON format and minify the JSON without including any explanation: ',
|
|
18
|
-
'{',
|
|
19
|
-
Object.entries(format).map(([key, value]) => {
|
|
20
|
-
return [
|
|
21
|
-
`/* ${value.desc} */`,
|
|
22
|
-
`"${key}": ${JSON.stringify(value.example)}`,
|
|
23
|
-
].join('\n')
|
|
24
|
-
}).join(',\n'),
|
|
25
|
-
'}'
|
|
26
|
-
].join('\n')
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* @zh 協助使用者將格式需求轉成論敘語句,支援 Handlebars。
|
|
31
|
-
* @en Assist the user in converting the formatting requirements into declarative sentences, support Handlebars.
|
|
32
|
-
*/
|
|
33
|
-
|
|
34
|
-
export const requireJsonResponseWithHandlebars = (
|
|
35
|
-
value: Record<string, any>,
|
|
36
|
-
question: string | string[],
|
|
37
|
-
format: Record<string, JsonResponseFormat>
|
|
38
|
-
) => {
|
|
39
|
-
const handlebars = Handlebars.create()
|
|
40
|
-
|
|
41
|
-
handlebars.registerHelper('DATA', function(this: any, value) {
|
|
42
|
-
return JSON.stringify(value)
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
handlebars.registerHelper('ENV', function(this: any, value) {
|
|
46
|
-
return this.__envs && value ? this.__envs[value] : ''
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
handlebars.registerHelper('INPUT', function(this: any) {
|
|
50
|
-
return JSON.stringify(record.omit(this, ['__envs']))
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
handlebars.registerHelper('JOIN', function(this: any, value) {
|
|
54
|
-
return Array.isArray(value) ? value.join() : JSON.stringify(value)
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
return handlebars.compile(requireJsonResponse(question, format))(value)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* @zh 協助使用者將格式需求轉成論敘語句,並支援 Json Schema。
|
|
62
|
-
* @en Assist the user in converting the formatting requirements into declarative sentences, and support Json Schema.
|
|
63
|
-
*/
|
|
64
|
-
|
|
65
|
-
export const requireJsonResponseWithJsonSchema = (question: string | string[], format: Record<string, any>) => {
|
|
66
|
-
return [
|
|
67
|
-
...(Array.isArray(question) ? question : [question]),
|
|
68
|
-
'Please provide JSON data according to the following JSON Schema format:',
|
|
69
|
-
JSON.stringify(format)
|
|
70
|
-
].join('\n')
|
|
71
|
-
}
|
package/lib/types.ts
DELETED
package/lib/utils/error.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
type ParserFail = {
|
|
2
|
-
name: string
|
|
3
|
-
error: any
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export class ParserError {
|
|
7
|
-
isParserError = true
|
|
8
|
-
parserFails: ParserFail[] = []
|
|
9
|
-
error: any
|
|
10
|
-
constructor(error: any, parserFails: ParserFail[]) {
|
|
11
|
-
this.error = error
|
|
12
|
-
this.parserFails = parserFails
|
|
13
|
-
}
|
|
14
|
-
}
|
package/lib/utils/validate.ts
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import * as Yup from 'yup'
|
|
2
|
-
import { convertSchema } from '@sodaru/yup-to-json-schema'
|
|
3
|
-
import { Schema } from 'yup'
|
|
4
|
-
|
|
5
|
-
export type ValidateCallback<T extends Record<string, Schema>> = (_yup: typeof Yup) => {
|
|
6
|
-
[K in keyof T]: T[K]
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export type ValidateCallbackOutputs<
|
|
10
|
-
T extends ValidateCallback<any>,
|
|
11
|
-
R = ReturnType<T>
|
|
12
|
-
> = {
|
|
13
|
-
[K in keyof R]: R[K] extends { '__outputType': any } ? R[K]['__outputType'] : unknown
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function definedValidateSchema<T extends ValidateCallback<any>>(cb: T): T {
|
|
17
|
-
return cb
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function validate<
|
|
21
|
-
T extends ValidateCallback<any>,
|
|
22
|
-
R = ReturnType<T>
|
|
23
|
-
>(target: any, schemaCallback: T) {
|
|
24
|
-
return Yup.object(schemaCallback(Yup)).required().validateSync(target || {}) as {
|
|
25
|
-
[K in keyof R]: R[K] extends { '__outputType': any } ? R[K]['__outputType'] : unknown
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export const defineYupSchema = <T extends Record<string, Schema>>(cb: ValidateCallback<T>): T => {
|
|
30
|
-
return cb(Yup)
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export const validateToJsonSchema = <T extends ValidateCallback<any>>(cb: T) => {
|
|
34
|
-
const removeAllDefault = (schema: any) => {
|
|
35
|
-
if (schema.default) {
|
|
36
|
-
delete schema.default
|
|
37
|
-
}
|
|
38
|
-
if (schema.properties) {
|
|
39
|
-
for (let key in schema.properties) {
|
|
40
|
-
if (schema.properties[key].default) {
|
|
41
|
-
delete schema.properties[key].default
|
|
42
|
-
}
|
|
43
|
-
removeAllDefault(schema.properties[key])
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
if (schema.items) {
|
|
47
|
-
removeAllDefault(schema.items)
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
const addAllAdditionalProperties = (jsonSchema: any) => {
|
|
51
|
-
if (jsonSchema.type === 'object') {
|
|
52
|
-
jsonSchema.additionalProperties = false
|
|
53
|
-
for (const key in jsonSchema.properties) {
|
|
54
|
-
addAllAdditionalProperties(jsonSchema.properties[key])
|
|
55
|
-
}
|
|
56
|
-
} else if (jsonSchema.type === 'array') {
|
|
57
|
-
addAllAdditionalProperties(jsonSchema.items)
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
const jsonSchema = convertSchema(Yup.object(cb(Yup)))
|
|
61
|
-
removeAllDefault(jsonSchema)
|
|
62
|
-
addAllAdditionalProperties(jsonSchema)
|
|
63
|
-
return jsonSchema
|
|
64
|
-
}
|