ai-functions 0.0.5 → 0.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/example.js +17 -5
- package/fetcher.js +48 -0
- package/index.js +1 -1
- package/package.json +2 -5
- package/{ai.js → proxy.js} +43 -27
- package/withAI.js +1 -1
package/example.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AI } from './
|
|
1
|
+
import { AI } from './proxy.js'
|
|
2
2
|
|
|
3
3
|
const { ai, list, gpt } = AI()
|
|
4
4
|
|
|
@@ -16,11 +16,23 @@ const writeBlogPost = title => gpt`write a blog post in markdown starting with "
|
|
|
16
16
|
|
|
17
17
|
async function* writeBlog(count, topic) {
|
|
18
18
|
for await (const title of listBlogPosts(count, topic)) {
|
|
19
|
-
const
|
|
20
|
-
|
|
19
|
+
const contentPromise = writeBlogPost(title).then(content => {
|
|
20
|
+
console.log({ title, content })
|
|
21
|
+
return { title, content }
|
|
22
|
+
})
|
|
23
|
+
yield { title, contentPromise }
|
|
21
24
|
}
|
|
22
25
|
}
|
|
23
26
|
|
|
24
|
-
for await (const post of writeBlog(
|
|
27
|
+
for await (const post of writeBlog(3, 'future of car sales')) {
|
|
25
28
|
console.log({ post })
|
|
26
|
-
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const product = await ai.categorizeProduct({ domain: 'OpenSaaS.org' }, {
|
|
32
|
+
productType: 'App | API | Marketplace | Platform | Packaged Service | Professional Service | Website',
|
|
33
|
+
customer: 'ideal customer profile in 3-5 words',
|
|
34
|
+
solution: 'describe the offer in 4-10 words',
|
|
35
|
+
description: 'website meta description',
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
console.log({ product })
|
package/fetcher.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const headers = {
|
|
2
|
+
'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`,
|
|
3
|
+
'Content-Type': 'application/json',
|
|
4
|
+
}
|
|
5
|
+
const openaiFetch = obj => fetch('https://api.openai.com/v1/chat/completions', { method: 'POST', headers, body: JSON.stringify(obj) }).then(res => res.json())
|
|
6
|
+
|
|
7
|
+
const openaiStream = obj => fetch('https://api.openai.com/v1/chat/completions', { method: 'POST', headers, body: JSON.stringify(obj) })
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
const response = await openaiStream({
|
|
11
|
+
model: 'gpt-3.5-turbo',
|
|
12
|
+
messages: [{ role: 'user', content: 'Tell me a funny joke about OpenAI' }],
|
|
13
|
+
stream: true
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
const decoder = new TextDecoder('utf-8')
|
|
17
|
+
let completion = ''
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
for await (const chunk of response.body) {
|
|
21
|
+
let done = false
|
|
22
|
+
const currentChunk = decoder.decode(chunk)
|
|
23
|
+
const lines = currentChunk.split('\n').filter(Boolean)
|
|
24
|
+
// console.log(lines)
|
|
25
|
+
for (const line of lines) {
|
|
26
|
+
if (line.includes('[DONE]')) {
|
|
27
|
+
done = true
|
|
28
|
+
break
|
|
29
|
+
}
|
|
30
|
+
try {
|
|
31
|
+
const data = JSON.parse(line.replace('data: ', ''))
|
|
32
|
+
if (data.choices[0].delta.content) {
|
|
33
|
+
const deltaContent = data.choices[0].delta.content
|
|
34
|
+
completion += deltaContent
|
|
35
|
+
console.log(deltaContent)
|
|
36
|
+
}
|
|
37
|
+
// console.log(chunks)
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.log(error.message, line)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (done) break
|
|
43
|
+
}
|
|
44
|
+
} catch (err) {
|
|
45
|
+
console.error(err.stack);
|
|
46
|
+
}
|
|
47
|
+
console.log({ completion })
|
|
48
|
+
|
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-functions",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Library for Developing and Managing AI Functions (including OpenAI GPT4 / GPT3.5)",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -23,10 +23,7 @@
|
|
|
23
23
|
"vitest": "^0.33.0"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"camelcase-keys": "^9.1.0",
|
|
27
26
|
"js-yaml": "^4.1.0",
|
|
28
|
-
"
|
|
29
|
-
"partial-json-parser": "^1.2.2",
|
|
30
|
-
"yaml": "^2.3.2"
|
|
27
|
+
"partial-json-parser": "^1.2.2"
|
|
31
28
|
}
|
|
32
29
|
}
|
package/{ai.js → proxy.js}
RENAMED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
import { OpenAI } from 'openai'
|
|
2
|
-
import camelcaseKeys from 'camelcase-keys'
|
|
1
|
+
// import { OpenAI } from 'openai'
|
|
2
|
+
// import camelcaseKeys from 'camelcase-keys'
|
|
3
3
|
import { dump } from 'js-yaml'
|
|
4
4
|
import { schema } from './schema.js'
|
|
5
5
|
|
|
6
6
|
export const AI = opts => {
|
|
7
7
|
const { system, model = 'gpt-3.5-turbo', apiKey, OPENAI_API_KEY, ...rest } = opts || {}
|
|
8
8
|
|
|
9
|
-
const openai = new OpenAI({ apiKey: apiKey ?? OPENAI_API_KEY, ...rest })
|
|
10
|
-
|
|
9
|
+
// const openai = new OpenAI({ apiKey: apiKey ?? OPENAI_API_KEY, ...rest })
|
|
10
|
+
const headers = {
|
|
11
|
+
'Authorization': `Bearer ${apiKey ?? OPENAI_API_KEY ?? globalThis.process?.env?.OPENAI_API_KEY}`,
|
|
12
|
+
'Content-Type': 'application/json',
|
|
13
|
+
}
|
|
14
|
+
const openaiFetch = obj => fetch('https://api.openai.com/v1/chat/completions', { method: 'POST', headers, body: JSON.stringify(obj) }).then(res => res.json())
|
|
15
|
+
|
|
11
16
|
const gpt = async (strings, ...values) => {
|
|
12
17
|
const user = values.map((value, i) => strings[i] + value).join('') + strings[strings.length - 1]
|
|
13
18
|
const prompt = {
|
|
@@ -17,7 +22,7 @@ export const AI = opts => {
|
|
|
17
22
|
],
|
|
18
23
|
}
|
|
19
24
|
if (system) prompt.messages.unshift({ role: 'system', content: system })
|
|
20
|
-
const completion = await
|
|
25
|
+
const completion = await openaiFetch(prompt)
|
|
21
26
|
return completion.choices?.[0].message.content
|
|
22
27
|
}
|
|
23
28
|
|
|
@@ -39,7 +44,7 @@ export const AI = opts => {
|
|
|
39
44
|
...rest,
|
|
40
45
|
}
|
|
41
46
|
if (system) prompt.messages.unshift({ role: 'system', content: system })
|
|
42
|
-
const completion = await
|
|
47
|
+
const completion = await openaiFetch(prompt)
|
|
43
48
|
let data, error
|
|
44
49
|
const { message } = completion.choices?.[0]
|
|
45
50
|
prompt.messages.push(message)
|
|
@@ -55,7 +60,7 @@ export const AI = opts => {
|
|
|
55
60
|
const cost = Math.round((gpt4
|
|
56
61
|
? completion.usage.prompt_tokens * 0.003 + completion.usage.completion_tokens * 0.006
|
|
57
62
|
: completion.usage.prompt_tokens * 0.00015 + completion.usage.completion_tokens * 0.0002) * 100000) / 100000
|
|
58
|
-
completion.usage = camelcaseKeys(completion.usage)
|
|
63
|
+
// completion.usage = camelcaseKeys(completion.usage)
|
|
59
64
|
console.log({ data, content, error, cost })
|
|
60
65
|
return meta ? { prompt, content, data, error, cost, ...completion } : data ?? content
|
|
61
66
|
}
|
|
@@ -70,36 +75,47 @@ export const AI = opts => {
|
|
|
70
75
|
stream: true,
|
|
71
76
|
}
|
|
72
77
|
if (system) prompt.messages.unshift({ role: 'system', content: system })
|
|
73
|
-
const
|
|
78
|
+
const response = await fetch('https://api.openai.com/v1/chat/completions', { method: 'POST', headers, body: JSON.stringify(prompt) })
|
|
79
|
+
const decoder = new TextDecoder('utf-8')
|
|
80
|
+
|
|
74
81
|
let content = ''
|
|
75
82
|
let seperator = undefined
|
|
76
83
|
let numberedList = undefined
|
|
77
84
|
|
|
78
|
-
for await (const
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
+
for await (const chunk of response.body) {
|
|
86
|
+
const lines = decoder.decode(chunk).split('\n').filter(Boolean)
|
|
87
|
+
for (const line of lines) {
|
|
88
|
+
try {
|
|
89
|
+
if (line.includes('[DONE]')) break
|
|
90
|
+
const part = JSON.parse(line.replace('data: ', ''))
|
|
91
|
+
const { delta, finish_reason } = part.choices[0]
|
|
92
|
+
content += delta?.content || ''
|
|
93
|
+
if (seperator === undefined && content.length > 4) {
|
|
94
|
+
numberedList = content.match(/(\d+\.\s)/g)
|
|
95
|
+
seperator = numberedList ? '\n' : ', '
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const numberedRegex = /\d+\.\s(?:")?([^"]+)(?:")?/
|
|
85
99
|
|
|
86
|
-
|
|
100
|
+
if (content.includes(seperator)) {
|
|
101
|
+
// get the string before the newline, and modify `content` to be the string after the newline
|
|
102
|
+
// then yield the string before the newline
|
|
103
|
+
const items = content.split(seperator)
|
|
104
|
+
while (items.length > 1) {
|
|
105
|
+
const item = items.shift()
|
|
106
|
+
yield numberedList ? item.match(numberedRegex)?.[1] : item
|
|
107
|
+
}
|
|
108
|
+
content = items[0]
|
|
109
|
+
}
|
|
87
110
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const items = content.split(seperator)
|
|
92
|
-
while (items.length > 1) {
|
|
93
|
-
const item = items.shift()
|
|
94
|
-
yield numberedList ? item.match(numberedRegex)?.[1] : item
|
|
111
|
+
if (finish_reason) yield numberedList ? content.match(numberedRegex)?.[1] : content
|
|
112
|
+
} catch (error) {
|
|
113
|
+
console.log(error.message, line)
|
|
95
114
|
}
|
|
96
|
-
content = items[0]
|
|
97
115
|
}
|
|
98
|
-
|
|
99
|
-
if (finish_reason) yield numberedList ? content.match(numberedRegex)?.[1] : content
|
|
100
116
|
}
|
|
101
117
|
|
|
102
118
|
}
|
|
103
119
|
|
|
104
|
-
return { ai, list, gpt
|
|
120
|
+
return { ai, list, gpt }
|
|
105
121
|
}
|
package/withAI.js
CHANGED