Skip to content

Commit 19aff6d

Browse files
authored
[feat] [sdk] ptaas (prompt as a service), bump to 0.0.9 (#17)
* feat(cozeloop-ai): update prompt types * feat(cozeloop-ai): prompt as a service * feat(cozeloop-ai): add uts * feat(example): add ptaas * feat(cozeloop-ai): bump to 0.0.9 * feat(cozeloop-ai): missing COZELOOP_API_TOKEN in ut
1 parent 6940395 commit 19aff6d

38 files changed

+1457
-100
lines changed

cspell.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"packagejson",
1818
"prefault",
1919
"preinstall",
20+
"ptaas",
2021
"remeda",
2122
"traceparent",
2223
"tracestate",

examples/cozeloop-ai-node/src/practice/travel-plan.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { type ChatCompletionCreateParams } from 'openai/resources/chat';
22
import { OpenAI } from 'openai';
3-
import { cozeLoopTracer, PromptHub, SpanKind } from '@cozeloop/ai';
3+
import { cozeLoopTracer, PromptHub, type Span, SpanKind } from '@cozeloop/ai';
44

55
// initialize tracer globally
66
cozeLoopTracer.initialize({
@@ -23,7 +23,7 @@ async function callLLM(messages: ChatCompletionCreateParams['messages']) {
2323

2424
// wrap model as a span node with `cozeLoopTracer.traceable`
2525
return await cozeLoopTracer.traceable(
26-
async span => {
26+
async (span: Span) => {
2727
cozeLoopTracer.setInput(span, { messages });
2828

2929
const resp = await openai.chat.completions.create({
@@ -64,7 +64,7 @@ async function runTravelPlan(options: TravelPlanOptions) {
6464
const messages = hub.formatPrompt(prompt, { ...options });
6565

6666
// invoke model
67-
return callLLM(messages);
67+
return callLLM(messages as ChatCompletionCreateParams['messages']);
6868
}
6969

7070
async function run() {
@@ -77,7 +77,7 @@ async function run() {
7777
};
7878

7979
const result = await cozeLoopTracer.traceable(
80-
async span => {
80+
async (span: Span) => {
8181
cozeLoopTracer.setInput(span, options);
8282
const { choices } = await runTravelPlan(options);
8383

examples/cozeloop-ai-node/src/prompt/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { run as runMultiPart } from './with-multi-part';
22
import { run as runWithLabel } from './with-label';
33
import { run as runWithJinja } from './with-jinja';
4+
import { run as runPTaaS } from './ptaas';
45
import { run as runBasic } from './hub';
56

67
export async function run() {
@@ -9,6 +10,7 @@ export async function run() {
910
runWithJinja(),
1011
runMultiPart(),
1112
runWithLabel(),
13+
runPTaaS(),
1214
]);
1315

1416
process.exit(0);
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
import assert from 'node:assert';
2+
3+
import { PromptAsAService } from '@cozeloop/ai';
4+
5+
async function runWithNormal() {
6+
const model = new PromptAsAService({
7+
/** workspace id, use process.env.COZELOOP_WORKSPACE_ID when unprovided */
8+
// workspaceId: 'your_workspace_id',
9+
apiClient: {
10+
// baseURL: 'api_base_url',
11+
// token: 'your_api_token',
12+
},
13+
prompt: {
14+
prompt_key: 'CozeLoop_Travel_Master',
15+
version: '0.0.2',
16+
},
17+
});
18+
19+
// 1. model.invoke
20+
const reply = await model.invoke({
21+
messages: [{ role: 'user', content: '帮我规划轻松旅行' }],
22+
variables: {
23+
departure: '北京',
24+
destination: '上海',
25+
people_num: 2,
26+
days_num: 1,
27+
travel_theme: '亲子',
28+
},
29+
});
30+
31+
assert(reply?.message);
32+
assert(reply.usage);
33+
assert.strictEqual(reply.finish_reason, 'stop');
34+
35+
// 2. model.stream
36+
const replyStream = await model.stream({
37+
messages: [{ role: 'user', content: '帮我规划轻松旅行' }],
38+
variables: {
39+
departure: '北京',
40+
destination: '上海',
41+
people_num: 2,
42+
days_num: 1,
43+
travel_theme: '亲子',
44+
},
45+
});
46+
47+
for await (const chunk of replyStream) {
48+
assert(chunk);
49+
}
50+
}
51+
52+
async function runWithJinja() {
53+
const model = new PromptAsAService({
54+
/** workspace id, use process.env.COZELOOP_WORKSPACE_ID when unprovided */
55+
// workspaceId: 'your_workspace_id',
56+
apiClient: {
57+
// baseURL: 'api_base_url',
58+
// token: 'your_api_token',
59+
},
60+
prompt: {
61+
prompt_key: 'loop12',
62+
version: '0.0.5',
63+
},
64+
});
65+
66+
// 1. model.invoke
67+
const reply = await model.invoke({
68+
messages: [{ role: 'user', content: '总结模板内容' }],
69+
variables: {
70+
title: 'Title',
71+
user: {
72+
is_authenticated: false,
73+
name: 'Loop',
74+
},
75+
items: [{ name: 'fish' }],
76+
place: [{ role: 'assistant', content: '好的' }],
77+
},
78+
});
79+
80+
assert(reply?.message);
81+
assert(reply.usage);
82+
assert.strictEqual(reply.finish_reason, 'stop');
83+
}
84+
85+
async function runWithMultiPart() {
86+
const model = new PromptAsAService({
87+
/** workspace id, use process.env.COZELOOP_WORKSPACE_ID when unprovided */
88+
// workspaceId: 'your_workspace_id',
89+
apiClient: {
90+
// baseURL: 'api_base_url',
91+
// token: 'your_api_token',
92+
},
93+
prompt: {
94+
prompt_key: 'loop',
95+
version: '0.0.3',
96+
},
97+
});
98+
99+
const replyStream = await model.stream({
100+
messages: [{ role: 'user', content: 'respond in 50 words' }],
101+
variables: {
102+
var1: 'sports',
103+
placeholder1: { role: 'assistant', content: 'go on' },
104+
var2: 'how to play football',
105+
img1: [
106+
{ type: 'text', text: 'text1' },
107+
{
108+
type: 'image_url',
109+
image_url: {
110+
url: 'https://tinypng.com/static/images/george-anim/large_george_x2.webp',
111+
},
112+
},
113+
],
114+
},
115+
});
116+
117+
for await (const chunk of replyStream) {
118+
assert(chunk);
119+
}
120+
}
121+
122+
export async function run() {
123+
await Promise.all([
124+
runWithNormal(), // 普通模板
125+
runWithJinja(), // Jinja2 模板
126+
runWithMultiPart(), // 多模态变量
127+
]);
128+
129+
process.exit();
130+
}
131+
132+
run();

packages/cozeloop-ai/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# 🕗 ChangeLog - @cozeloop/ai
22

3+
## 0.0.9
4+
* Prompt as a Service (ptaas)
5+
36
## 0.0.8
47
* PromptHub: get prompt with label
58

packages/cozeloop-ai/README.md

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,30 +19,44 @@ pnpm install @cozeloop/ai
1919

2020
### 2. Basic Usage
2121
```typescript
22-
import { ApiClient, PromptHub } from '@cozeloop/ai';
22+
import { ApiClient, PromptHub, PromptAsAService } from '@cozeloop/ai';
2323

24-
// 1. setup API client
24+
// 1. Setup API client
2525
const apiClient = new ApiClient({
2626
baseURL: 'https://api.coze.cn',
2727
token: 'your_access_token',
2828
});
2929

30-
// 2. Using prompt hub to get prompt
31-
const promptHub = new PromptHub({
30+
// 2. Using `PromptHub` or `PromptAsAService`
31+
const hub = new PromptHub({
3232
// or set it as process.env.COZELOOP_WORKSPACE_ID,
3333
workspaceId: 'your_workspace_id',
3434
apiClient,
3535
});
36+
// hub.getPrompt(key, version);
37+
// hub.formatPrompt(prompt);
3638

37-
const prompt = await promptHub.getPrompt(
38-
'your_prompt_key',
39-
'prompt_version (optional)',
40-
);
39+
const model = new PromptAsAService({
40+
// or set it as process.env.COZELOOP_WORKSPACE_ID,
41+
workspaceId: 'your_workspace_id',
42+
// prompt to invoke as a service
43+
prompt: {
44+
prompt_key: 'your_prompt_key',
45+
},
46+
apiClient,
47+
});
48+
// model.invoke({
49+
// messages: [{ role: 'user', content: 'hi' }],
50+
// });
51+
// model.stream({
52+
// messages: [{ role: 'user', content: 'hi' }],
53+
// });
4154
```
4255

4356
## Key Features
44-
- 🗄️ **Prompt Hub**: Develop, submit and publish prompts on [CozeLoop](https://loop.coze.cn), and access them it via `PromptHub`
45-
- 🔐 **Authentication Methods**: PAT and JWT
57+
- 🗂️ **Prompt Hub**: Develop, submit and publish prompts on [CozeLoop](https://loop.coze.cn), and access them via `PromptHub`
58+
- 🛠️ **Prompt as a Service**: Develop, submit and publish prompts on [CozeLoop](https://loop.coze.cn), and invoke them as services
59+
- 🔐 **Authentication Methods**: PAT, SAT and JWT
4660
- ⚙️ **Configurable**: Timeout, headers, signal, debug options
4761

4862
## Authentication Options

packages/cozeloop-ai/README.zh-CN.md

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,29 +19,43 @@ pnpm install @cozeloop/ai
1919

2020
### 2. 基础用法
2121
```typescript
22-
import { ApiClient, PromptHub } from '@cozeloop/ai';
22+
import { ApiClient, PromptHub, PromptAsAService } from '@cozeloop/ai';
2323

2424
// 1. 设置 ApiClient
2525
const apiClient = new ApiClient({
2626
baseURL: 'https://api.coze.cn',
2727
token: 'your_access_token',
2828
});
2929

30-
// 2. 使用 PromptHub 获取 Prompt
30+
// 2. 使用 `PromptHub` 或 `PromptAsAService`
3131
const promptHub = new PromptHub({
3232
// 或设置环境变量 process.env.COZELOOP_WORKSPACE_ID,
3333
workspaceId: 'your_workspace_id',
3434
apiClient,
3535
});
36+
// hub.getPrompt(key, version);
37+
// hub.formatPrompt(prompt);
3638

37-
const prompt = await promptHub.getPrompt(
38-
'your_prompt_key',
39-
'prompt_version (optional)',
40-
);
39+
const model = new PromptAsAService({
40+
// 或设置环境变量 process.env.COZELOOP_WORKSPACE_ID,
41+
workspaceId: 'your_workspace_id',
42+
// 要调用的 prompt
43+
prompt: {
44+
prompt_key: 'your_prompt_key',
45+
},
46+
apiClient,
47+
});
48+
// model.invoke({
49+
// messages: [{ role: 'user', content: 'hi' }],
50+
// });
51+
// model.stream({
52+
// messages: [{ role: 'user', content: 'hi' }],
53+
// });
4154
```
4255

4356
## 主要特性
44-
- 🗄️ **Prompt Hub**: 在 [CozeLoop](https://loop.coze.cn) 平台开发、提交和发布 Prompt,使用 `PromptHub` 访问 Prompt。
57+
- 🗂️ **Prompt Hub**: 在 [CozeLoop](https://loop.coze.cn) 平台开发、提交和发布 Prompt,使用 `PromptHub` 访问 Prompt。
58+
- 🛠️ **Prompt as a Service**: 在 [CozeLoop](https://loop.coze.cn) 平台开发、提交和发布 Prompt,并作为服务调用。
4559
- 🔐 **多种鉴权方式**: PAT and JWT
4660
- ⚙️ **可配置**: 超时、请求头、信号、调试
4761

packages/cozeloop-ai/__tests__/__mock__/base-http.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ import { fileToStreamResp, headersToJson, setupMockServer } from './utils';
99

1010
export function setupBaseHttpMock() {
1111
const mockServer = setupServer(
12+
http.post(/\/stream-event-error/i, () =>
13+
fileToStreamResp(join(__dirname, 'base-stream-event-error.txt')),
14+
),
15+
http.post(/\/stream-parse-error/i, () =>
16+
fileToStreamResp(join(__dirname, 'base-stream-parse-error.txt')),
17+
),
1218
http.post(/\/stream/i, () =>
1319
fileToStreamResp(join(__dirname, 'base-stream.txt')),
1420
),
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
data: {"seq": 1}
2+
3+
event: error
4+
data: 500 Bad Gateway
5+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
data: {"seq"1: 1}
2+

0 commit comments

Comments
 (0)