@cloudbase/agent-adapter-yuanqi 1.0.1-alpha.12
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 +167 -0
- package/dist/index.d.mts +49 -0
- package/dist/index.d.ts +49 -0
- package/dist/index.js +330 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +294 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +56 -0
package/README.md
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# @cloudbase/agent-adapter-yuanqi
|
|
2
|
+
|
|
3
|
+
Tencent Yuanqi adapter for AG-Kit agents. This package provides integration between AG-Kit and [Tencent Yuanqi](https://yuanqi.tencent.com/), enabling you to use Yuanqi AI agents with AG-Kit's agent infrastructure.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- ✅ **YuanqiAgent**: Agent implementation that connects to Tencent Yuanqi AI agents
|
|
8
|
+
- ✅ **Streaming Support**: Real-time streaming responses via OpenAI-compatible API
|
|
9
|
+
- ✅ **Thinking/Reasoning Support**: Handles reasoning content from Yuanqi models with thinking events
|
|
10
|
+
- ✅ **Custom Variables**: Pass custom parameters to Yuanqi agents
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install @cloudbase/agent-adapter-yuanqi
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Environment Variables
|
|
19
|
+
|
|
20
|
+
Configure the following environment variables:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
YUANQI_APP_ID=your-yuanqi-assistant-id # Yuanqi assistant ID (required)
|
|
24
|
+
YUANQI_APP_KEY=your-yuanqi-app-key # Yuanqi application key (required)
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
### Basic Agent Setup
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { YuanqiAgent } from "@cloudbase/agent-adapter-yuanqi";
|
|
33
|
+
|
|
34
|
+
const agent = new YuanqiAgent({
|
|
35
|
+
yuanqiConfig: {
|
|
36
|
+
appId: process.env.YUANQI_APP_ID, // Or set via YUANQI_APP_ID env
|
|
37
|
+
appKey: process.env.YUANQI_APP_KEY, // Or set via YUANQI_APP_KEY env
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### With Custom Configuration
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
import { YuanqiAgent } from "@cloudbase/agent-adapter-yuanqi";
|
|
46
|
+
|
|
47
|
+
const agent = new YuanqiAgent({
|
|
48
|
+
yuanqiConfig: {
|
|
49
|
+
appId: "your-assistant-id",
|
|
50
|
+
appKey: "your-app-key",
|
|
51
|
+
request: {
|
|
52
|
+
baseUrl: "https://yuanqi.tencent.com/openapi/v1/agent", // Default base URL
|
|
53
|
+
body: {
|
|
54
|
+
chatType: "published", // or "preview"
|
|
55
|
+
customVariables: {
|
|
56
|
+
key1: "value1",
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Running the Agent
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { randomUUID } from "crypto";
|
|
68
|
+
|
|
69
|
+
const runId = randomUUID();
|
|
70
|
+
const threadId = randomUUID();
|
|
71
|
+
|
|
72
|
+
const observable = agent.run({
|
|
73
|
+
runId,
|
|
74
|
+
threadId,
|
|
75
|
+
messages: [
|
|
76
|
+
{
|
|
77
|
+
id: randomUUID(),
|
|
78
|
+
role: "user",
|
|
79
|
+
content: "Hello, how can you help me?",
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
forwardedProps: {
|
|
83
|
+
userId: "user-123", // Optional: custom user ID
|
|
84
|
+
customVariables: {
|
|
85
|
+
key1: "value1",
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
observable.subscribe({
|
|
91
|
+
next: (event) => console.log(event),
|
|
92
|
+
complete: () => console.log("Done"),
|
|
93
|
+
error: (err) => console.error(err),
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## API Reference
|
|
98
|
+
|
|
99
|
+
### YuanqiAgent
|
|
100
|
+
|
|
101
|
+
Agent class that extends `AbstractAgent` from `@ag-ui/client` and connects to Tencent Yuanqi services.
|
|
102
|
+
|
|
103
|
+
**Constructor:**
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
constructor(config: AgentConfig & { yuanqiConfig: YuanqiConfig })
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### YuanqiConfig
|
|
110
|
+
|
|
111
|
+
Configuration options for the Yuanqi adapter.
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
interface YuanqiConfig {
|
|
115
|
+
appId?: string; // Yuanqi assistant ID (optional if YUANQI_APP_ID env is set)
|
|
116
|
+
appKey?: string; // Yuanqi app key (optional if YUANQI_APP_KEY env is set)
|
|
117
|
+
request?: {
|
|
118
|
+
baseUrl?: string; // Base URL (default: https://yuanqi.tencent.com/openapi/v1/agent)
|
|
119
|
+
body?: Partial<YuanqiChatRequest>; // Additional request body options
|
|
120
|
+
headers?: Record<string, string>; // Additional request headers
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### YuanqiChatRequest
|
|
126
|
+
|
|
127
|
+
Request body options for Yuanqi chat API.
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
interface YuanqiChatRequest {
|
|
131
|
+
assistantId: string; // Yuanqi assistant ID
|
|
132
|
+
userId: string; // User identifier
|
|
133
|
+
messages: ChatMessage[]; // Chat messages
|
|
134
|
+
stream?: boolean; // Enable streaming (default: true)
|
|
135
|
+
customVariables?: Record<string, string>; // Custom variables for the assistant
|
|
136
|
+
version?: number; // Assistant version
|
|
137
|
+
chatType?: "published" | "preview"; // Chat mode
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Supported Events
|
|
142
|
+
|
|
143
|
+
The adapter emits the following AG-UI events:
|
|
144
|
+
|
|
145
|
+
**Run Lifecycle:**
|
|
146
|
+
- `RUN_STARTED` / `RUN_FINISHED` / `RUN_ERROR` - Run lifecycle events
|
|
147
|
+
|
|
148
|
+
**Text Response:**
|
|
149
|
+
- `TEXT_MESSAGE_START` / `TEXT_MESSAGE_CONTENT` / `TEXT_MESSAGE_END` - Streaming text response
|
|
150
|
+
|
|
151
|
+
**Thinking/Reasoning (for reasoning models):**
|
|
152
|
+
- `THINKING_START` / `THINKING_END` - Thinking block boundaries
|
|
153
|
+
- `THINKING_TEXT_MESSAGE_START` / `THINKING_TEXT_MESSAGE_CONTENT` / `THINKING_TEXT_MESSAGE_END` - Thinking content
|
|
154
|
+
|
|
155
|
+
**Tool Calls:**
|
|
156
|
+
- `TOOL_CALL_START` / `TOOL_CALL_ARGS` / `TOOL_CALL_END` - Tool call events
|
|
157
|
+
|
|
158
|
+
## Dependencies
|
|
159
|
+
|
|
160
|
+
- `@ag-ui/client`: AG-UI client protocol (provides `AbstractAgent` base class)
|
|
161
|
+
- `openai`: OpenAI SDK for API compatibility
|
|
162
|
+
- `rxjs`: Reactive extensions for JavaScript
|
|
163
|
+
|
|
164
|
+
## Related Resources
|
|
165
|
+
|
|
166
|
+
- [Tencent Yuanqi](https://yuanqi.tencent.com/)
|
|
167
|
+
- [Yuanqi API Documentation](https://yuanqi.tencent.com/guide/publish-agent-api-documentation)
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { AbstractAgent, AgentConfig, RunAgentInput, BaseEvent, Message } from '@ag-ui/client';
|
|
2
|
+
import OpenAI from 'openai';
|
|
3
|
+
import { Observable } from 'rxjs';
|
|
4
|
+
|
|
5
|
+
interface YuanqiConfig {
|
|
6
|
+
request?: {
|
|
7
|
+
baseUrl?: string;
|
|
8
|
+
body?: Partial<YuanqiChatRequest>;
|
|
9
|
+
headers?: Record<string, string>;
|
|
10
|
+
};
|
|
11
|
+
appId?: string;
|
|
12
|
+
appKey?: string;
|
|
13
|
+
}
|
|
14
|
+
type ChatMessage = OpenAI.Chat.Completions.ChatCompletionMessageParam;
|
|
15
|
+
interface YuanqiChatRequest {
|
|
16
|
+
assistantId: string;
|
|
17
|
+
userId: string;
|
|
18
|
+
messages: ChatMessage[];
|
|
19
|
+
stream?: boolean;
|
|
20
|
+
customVariables?: Record<string, string>;
|
|
21
|
+
version?: number;
|
|
22
|
+
chatType?: "published" | "preview";
|
|
23
|
+
[key: string]: any;
|
|
24
|
+
}
|
|
25
|
+
declare class YuanqiAgent extends AbstractAgent {
|
|
26
|
+
protected yuanqiConfig: YuanqiConfig;
|
|
27
|
+
private finalAppId;
|
|
28
|
+
private model;
|
|
29
|
+
constructor(config: AgentConfig & {
|
|
30
|
+
yuanqiConfig: YuanqiConfig;
|
|
31
|
+
});
|
|
32
|
+
generateRequestBody({ messages, input, }: {
|
|
33
|
+
messages: ChatMessage[];
|
|
34
|
+
input: RunAgentInput;
|
|
35
|
+
}): YuanqiChatRequest;
|
|
36
|
+
run(input: RunAgentInput): Observable<BaseEvent>;
|
|
37
|
+
private _run;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Convert AGUI messages to OpenAI chat completion format
|
|
41
|
+
*/
|
|
42
|
+
declare function convertMessagesToOpenAI(messages: Message[], systemPrompt?: string): OpenAI.Chat.ChatCompletionMessageParam[];
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 递归地将对象的所有小驼峰属性名转换为下划线格式
|
|
46
|
+
*/
|
|
47
|
+
declare function camelToSnakeKeys<T>(obj: T): T;
|
|
48
|
+
|
|
49
|
+
export { type ChatMessage, YuanqiAgent, type YuanqiChatRequest, type YuanqiConfig, camelToSnakeKeys, convertMessagesToOpenAI };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { AbstractAgent, AgentConfig, RunAgentInput, BaseEvent, Message } from '@ag-ui/client';
|
|
2
|
+
import OpenAI from 'openai';
|
|
3
|
+
import { Observable } from 'rxjs';
|
|
4
|
+
|
|
5
|
+
interface YuanqiConfig {
|
|
6
|
+
request?: {
|
|
7
|
+
baseUrl?: string;
|
|
8
|
+
body?: Partial<YuanqiChatRequest>;
|
|
9
|
+
headers?: Record<string, string>;
|
|
10
|
+
};
|
|
11
|
+
appId?: string;
|
|
12
|
+
appKey?: string;
|
|
13
|
+
}
|
|
14
|
+
type ChatMessage = OpenAI.Chat.Completions.ChatCompletionMessageParam;
|
|
15
|
+
interface YuanqiChatRequest {
|
|
16
|
+
assistantId: string;
|
|
17
|
+
userId: string;
|
|
18
|
+
messages: ChatMessage[];
|
|
19
|
+
stream?: boolean;
|
|
20
|
+
customVariables?: Record<string, string>;
|
|
21
|
+
version?: number;
|
|
22
|
+
chatType?: "published" | "preview";
|
|
23
|
+
[key: string]: any;
|
|
24
|
+
}
|
|
25
|
+
declare class YuanqiAgent extends AbstractAgent {
|
|
26
|
+
protected yuanqiConfig: YuanqiConfig;
|
|
27
|
+
private finalAppId;
|
|
28
|
+
private model;
|
|
29
|
+
constructor(config: AgentConfig & {
|
|
30
|
+
yuanqiConfig: YuanqiConfig;
|
|
31
|
+
});
|
|
32
|
+
generateRequestBody({ messages, input, }: {
|
|
33
|
+
messages: ChatMessage[];
|
|
34
|
+
input: RunAgentInput;
|
|
35
|
+
}): YuanqiChatRequest;
|
|
36
|
+
run(input: RunAgentInput): Observable<BaseEvent>;
|
|
37
|
+
private _run;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Convert AGUI messages to OpenAI chat completion format
|
|
41
|
+
*/
|
|
42
|
+
declare function convertMessagesToOpenAI(messages: Message[], systemPrompt?: string): OpenAI.Chat.ChatCompletionMessageParam[];
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 递归地将对象的所有小驼峰属性名转换为下划线格式
|
|
46
|
+
*/
|
|
47
|
+
declare function camelToSnakeKeys<T>(obj: T): T;
|
|
48
|
+
|
|
49
|
+
export { type ChatMessage, YuanqiAgent, type YuanqiChatRequest, type YuanqiConfig, camelToSnakeKeys, convertMessagesToOpenAI };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
YuanqiAgent: () => YuanqiAgent,
|
|
34
|
+
camelToSnakeKeys: () => camelToSnakeKeys,
|
|
35
|
+
convertMessagesToOpenAI: () => convertMessagesToOpenAI
|
|
36
|
+
});
|
|
37
|
+
module.exports = __toCommonJS(index_exports);
|
|
38
|
+
|
|
39
|
+
// src/agent.ts
|
|
40
|
+
var import_client2 = require("@ag-ui/client");
|
|
41
|
+
var import_openai = __toESM(require("openai"));
|
|
42
|
+
var import_crypto = require("crypto");
|
|
43
|
+
|
|
44
|
+
// src/utils.ts
|
|
45
|
+
function camelToSnake(str) {
|
|
46
|
+
return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
47
|
+
}
|
|
48
|
+
function camelToSnakeKeys(obj) {
|
|
49
|
+
if (obj === null || obj === void 0) {
|
|
50
|
+
return obj;
|
|
51
|
+
}
|
|
52
|
+
if (Array.isArray(obj)) {
|
|
53
|
+
return obj.map((item) => camelToSnakeKeys(item));
|
|
54
|
+
}
|
|
55
|
+
if (typeof obj === "object") {
|
|
56
|
+
const result = {};
|
|
57
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
58
|
+
const snakeKey = camelToSnake(key);
|
|
59
|
+
result[snakeKey] = camelToSnakeKeys(value);
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
return obj;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// src/stream.ts
|
|
67
|
+
var import_client = require("@ag-ui/client");
|
|
68
|
+
async function* processYuanqiStream(stream, context) {
|
|
69
|
+
const { threadId, runId, messageId } = context;
|
|
70
|
+
const state = {
|
|
71
|
+
hasStarted: false,
|
|
72
|
+
fullContent: "",
|
|
73
|
+
toolCallsMap: /* @__PURE__ */ new Map()
|
|
74
|
+
};
|
|
75
|
+
const reasoningState = {
|
|
76
|
+
hasStarted: false,
|
|
77
|
+
fullContent: "",
|
|
78
|
+
toolCallsMap: /* @__PURE__ */ new Map()
|
|
79
|
+
};
|
|
80
|
+
for await (const chunk of stream) {
|
|
81
|
+
const delta = chunk.choices[0]?.delta;
|
|
82
|
+
if (!delta) continue;
|
|
83
|
+
if (delta.content) {
|
|
84
|
+
if (reasoningState.hasStarted) {
|
|
85
|
+
yield {
|
|
86
|
+
type: import_client.EventType.THINKING_TEXT_MESSAGE_END,
|
|
87
|
+
threadId,
|
|
88
|
+
runId,
|
|
89
|
+
messageId
|
|
90
|
+
};
|
|
91
|
+
yield {
|
|
92
|
+
type: import_client.EventType.THINKING_END,
|
|
93
|
+
threadId,
|
|
94
|
+
runId,
|
|
95
|
+
messageId
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
if (!state.hasStarted) {
|
|
99
|
+
yield {
|
|
100
|
+
type: import_client.EventType.TEXT_MESSAGE_START,
|
|
101
|
+
threadId,
|
|
102
|
+
runId,
|
|
103
|
+
messageId,
|
|
104
|
+
role: "assistant"
|
|
105
|
+
};
|
|
106
|
+
state.hasStarted = true;
|
|
107
|
+
}
|
|
108
|
+
state.fullContent += delta.content;
|
|
109
|
+
yield {
|
|
110
|
+
type: import_client.EventType.TEXT_MESSAGE_CONTENT,
|
|
111
|
+
threadId,
|
|
112
|
+
runId,
|
|
113
|
+
messageId,
|
|
114
|
+
delta: delta.content
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
if (delta.reasoning_content) {
|
|
118
|
+
if (!reasoningState.hasStarted) {
|
|
119
|
+
yield {
|
|
120
|
+
type: import_client.EventType.THINKING_START,
|
|
121
|
+
threadId,
|
|
122
|
+
runId,
|
|
123
|
+
messageId
|
|
124
|
+
};
|
|
125
|
+
yield {
|
|
126
|
+
type: import_client.EventType.THINKING_TEXT_MESSAGE_START,
|
|
127
|
+
threadId,
|
|
128
|
+
runId,
|
|
129
|
+
messageId,
|
|
130
|
+
role: "assistant"
|
|
131
|
+
};
|
|
132
|
+
reasoningState.hasStarted = true;
|
|
133
|
+
}
|
|
134
|
+
reasoningState.fullContent += delta.reasoning_content;
|
|
135
|
+
yield {
|
|
136
|
+
type: import_client.EventType.THINKING_TEXT_MESSAGE_CONTENT,
|
|
137
|
+
threadId,
|
|
138
|
+
runId,
|
|
139
|
+
messageId,
|
|
140
|
+
delta: delta.reasoning_content
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
if (delta.tool_calls) {
|
|
144
|
+
for (const toolCall of delta.tool_calls) {
|
|
145
|
+
const toolCallId = toolCall.id || `tool_${toolCall.index}`;
|
|
146
|
+
if (toolCall.function?.name) {
|
|
147
|
+
yield {
|
|
148
|
+
type: import_client.EventType.TOOL_CALL_START,
|
|
149
|
+
threadId,
|
|
150
|
+
runId,
|
|
151
|
+
toolCallId,
|
|
152
|
+
toolCallName: toolCall.function.name
|
|
153
|
+
};
|
|
154
|
+
state.toolCallsMap.set(toolCallId, {
|
|
155
|
+
name: toolCall.function.name,
|
|
156
|
+
args: toolCall.function.arguments || ""
|
|
157
|
+
});
|
|
158
|
+
} else if (toolCall.function?.arguments) {
|
|
159
|
+
const existing = state.toolCallsMap.get(toolCallId);
|
|
160
|
+
if (existing) {
|
|
161
|
+
existing.args += toolCall.function.arguments;
|
|
162
|
+
yield {
|
|
163
|
+
type: import_client.EventType.TOOL_CALL_ARGS,
|
|
164
|
+
threadId,
|
|
165
|
+
runId,
|
|
166
|
+
toolCallId,
|
|
167
|
+
delta: toolCall.function.arguments
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (state.hasStarted) {
|
|
175
|
+
yield {
|
|
176
|
+
type: import_client.EventType.TEXT_MESSAGE_END,
|
|
177
|
+
threadId,
|
|
178
|
+
runId,
|
|
179
|
+
messageId
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
for (const [toolCallId] of state.toolCallsMap) {
|
|
183
|
+
yield {
|
|
184
|
+
type: import_client.EventType.TOOL_CALL_END,
|
|
185
|
+
threadId,
|
|
186
|
+
runId,
|
|
187
|
+
toolCallId
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
yield {
|
|
191
|
+
type: import_client.EventType.RUN_FINISHED,
|
|
192
|
+
threadId,
|
|
193
|
+
runId
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// src/agent.ts
|
|
198
|
+
var import_rxjs = require("rxjs");
|
|
199
|
+
var YuanqiAgent = class extends import_client2.AbstractAgent {
|
|
200
|
+
constructor(config) {
|
|
201
|
+
super(config);
|
|
202
|
+
this.yuanqiConfig = config.yuanqiConfig;
|
|
203
|
+
this.model = new import_openai.default({
|
|
204
|
+
apiKey: "",
|
|
205
|
+
baseURL: this.yuanqiConfig.request?.baseUrl || "https://yuanqi.tencent.com/openapi/v1/agent"
|
|
206
|
+
});
|
|
207
|
+
this.finalAppId = this.yuanqiConfig.appId || this.yuanqiConfig.request?.body?.assistantId || process.env.YUANQI_APP_ID || "";
|
|
208
|
+
}
|
|
209
|
+
generateRequestBody({
|
|
210
|
+
messages,
|
|
211
|
+
input
|
|
212
|
+
}) {
|
|
213
|
+
const { state, forwardedProps } = input;
|
|
214
|
+
const requestBody = {
|
|
215
|
+
stream: true,
|
|
216
|
+
...this.yuanqiConfig.request?.body || {},
|
|
217
|
+
...forwardedProps || {},
|
|
218
|
+
assistantId: this.finalAppId,
|
|
219
|
+
userId: state?.__request_context__?.id || forwardedProps?.userId || (0, import_crypto.randomUUID)(),
|
|
220
|
+
messages
|
|
221
|
+
};
|
|
222
|
+
return requestBody;
|
|
223
|
+
}
|
|
224
|
+
run(input) {
|
|
225
|
+
return new import_rxjs.Observable((subscriber) => {
|
|
226
|
+
this._run(subscriber, input);
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
async _run(subscriber, input) {
|
|
230
|
+
try {
|
|
231
|
+
const { messages, runId, threadId, forwardedProps } = input;
|
|
232
|
+
subscriber.next({
|
|
233
|
+
type: import_client2.EventType.RUN_STARTED,
|
|
234
|
+
threadId,
|
|
235
|
+
runId
|
|
236
|
+
});
|
|
237
|
+
if (!this.finalAppId) {
|
|
238
|
+
throw new Error(
|
|
239
|
+
"YUANQI_APP_ID is required, check your env variables or config passed with the adapter"
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
if (!this.yuanqiConfig.appKey && !process.env.YUANQI_APP_KEY) {
|
|
243
|
+
throw new Error(
|
|
244
|
+
"YUANQI_APP_KEY is required, check your env variables or config passed with the adapter"
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
const openai = this.model;
|
|
248
|
+
const openaiMessages = convertMessagesToOpenAI(messages);
|
|
249
|
+
const body = this.generateRequestBody({
|
|
250
|
+
messages: openaiMessages,
|
|
251
|
+
input
|
|
252
|
+
});
|
|
253
|
+
const stream = await openai.chat.completions.create(
|
|
254
|
+
{
|
|
255
|
+
stream: true,
|
|
256
|
+
messages: [],
|
|
257
|
+
model: ""
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
body: camelToSnakeKeys(body),
|
|
261
|
+
headers: {
|
|
262
|
+
...this.yuanqiConfig.request?.headers,
|
|
263
|
+
Authorization: `Bearer ${this.yuanqiConfig.appKey || process.env.YUANQI_APP_KEY}`
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
);
|
|
267
|
+
const messageId = `msg_${Date.now()}`;
|
|
268
|
+
const context = { threadId, runId, messageId };
|
|
269
|
+
for await (const event of processYuanqiStream(stream, context)) {
|
|
270
|
+
subscriber.next(event);
|
|
271
|
+
}
|
|
272
|
+
} catch (error) {
|
|
273
|
+
if (error instanceof Error) {
|
|
274
|
+
console.error(`Error ${error.name}: ${error.message}`);
|
|
275
|
+
} else {
|
|
276
|
+
console.error(JSON.stringify(error));
|
|
277
|
+
}
|
|
278
|
+
subscriber.next({
|
|
279
|
+
type: import_client2.EventType.RUN_ERROR,
|
|
280
|
+
message: error instanceof Error ? error.message : String(error),
|
|
281
|
+
code: error instanceof Error ? error.name : "UNKNOWN_ERROR"
|
|
282
|
+
});
|
|
283
|
+
subscriber.complete();
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
function convertMessagesToOpenAI(messages, systemPrompt) {
|
|
288
|
+
const openaiMessages = [];
|
|
289
|
+
if (systemPrompt) {
|
|
290
|
+
openaiMessages.push({
|
|
291
|
+
role: "system",
|
|
292
|
+
content: systemPrompt
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
for (const msg of messages) {
|
|
296
|
+
if (msg.role === "user") {
|
|
297
|
+
openaiMessages.push({
|
|
298
|
+
role: "user",
|
|
299
|
+
content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
|
|
300
|
+
});
|
|
301
|
+
} else if (msg.role === "assistant") {
|
|
302
|
+
openaiMessages.push({
|
|
303
|
+
role: "assistant",
|
|
304
|
+
content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content) || "",
|
|
305
|
+
tool_calls: msg.toolCalls?.map((tc) => ({
|
|
306
|
+
id: tc.id,
|
|
307
|
+
type: "function",
|
|
308
|
+
function: {
|
|
309
|
+
name: tc.function.name,
|
|
310
|
+
arguments: tc.function.arguments
|
|
311
|
+
}
|
|
312
|
+
}))
|
|
313
|
+
});
|
|
314
|
+
} else if (msg.role === "tool") {
|
|
315
|
+
openaiMessages.push({
|
|
316
|
+
role: "tool",
|
|
317
|
+
tool_call_id: msg.toolCallId,
|
|
318
|
+
content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return openaiMessages;
|
|
323
|
+
}
|
|
324
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
325
|
+
0 && (module.exports = {
|
|
326
|
+
YuanqiAgent,
|
|
327
|
+
camelToSnakeKeys,
|
|
328
|
+
convertMessagesToOpenAI
|
|
329
|
+
});
|
|
330
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/agent.ts","../src/utils.ts","../src/stream.ts"],"sourcesContent":["export * from \"./agent\";\n\nexport * from \"./utils\";\n","import {\n RunAgentInput,\n Message,\n AbstractAgent,\n AgentConfig,\n BaseEvent,\n EventType,\n} from \"@ag-ui/client\";\n// import tcb from \"@cloudbase/node-sdk\";\nimport OpenAI from \"openai\";\nimport { randomUUID } from \"crypto\";\nimport { camelToSnakeKeys } from \"./utils\";\nimport { processYuanqiStream } from \"./stream\";\nimport { Observable } from \"rxjs\";\n\nexport interface YuanqiConfig {\n request?: {\n baseUrl?: string;\n body?: Partial<YuanqiChatRequest>;\n headers?: Record<string, string>;\n };\n appId?: string;\n appKey?: string;\n}\n\nexport type ChatMessage = OpenAI.Chat.Completions.ChatCompletionMessageParam;\n\nexport interface YuanqiChatRequest {\n assistantId: string;\n userId: string;\n messages: ChatMessage[];\n stream?: boolean;\n customVariables?: Record<string, string>;\n version?: number;\n chatType?: \"published\" | \"preview\";\n [key: string]: any;\n}\n\nexport class YuanqiAgent extends AbstractAgent {\n protected yuanqiConfig: YuanqiConfig;\n private finalAppId: string;\n private model: OpenAI;\n constructor(config: AgentConfig & { yuanqiConfig: YuanqiConfig }) {\n super(config);\n this.yuanqiConfig = config.yuanqiConfig;\n this.model = new OpenAI({\n apiKey: \"\",\n baseURL:\n this.yuanqiConfig.request?.baseUrl ||\n \"https://yuanqi.tencent.com/openapi/v1/agent\",\n });\n this.finalAppId =\n this.yuanqiConfig.appId ||\n this.yuanqiConfig.request?.body?.assistantId ||\n process.env.YUANQI_APP_ID ||\n \"\";\n }\n\n generateRequestBody({\n messages,\n input,\n }: {\n messages: ChatMessage[];\n input: RunAgentInput;\n }): YuanqiChatRequest {\n const { state, forwardedProps } = input;\n const requestBody: YuanqiChatRequest = {\n stream: true,\n ...(this.yuanqiConfig.request?.body || {}),\n ...(forwardedProps || {}),\n\n assistantId: this.finalAppId,\n userId:\n state?.__request_context__?.id ||\n forwardedProps?.userId ||\n randomUUID(),\n messages,\n };\n return requestBody;\n }\n\n run(input: RunAgentInput): Observable<BaseEvent> {\n return new Observable<BaseEvent>((subscriber) => {\n this._run(subscriber, input);\n });\n }\n\n private async _run(subscriber: any, input: RunAgentInput): Promise<void> {\n try {\n const { messages, runId, threadId, forwardedProps } = input;\n\n subscriber.next({\n type: EventType.RUN_STARTED,\n threadId,\n runId,\n });\n\n // if (!getCloudbaseEnvId()) {\n // throw new Error(\n // \"CLOUDBASE_ENV_ID is required, check your env variables or config passed with the adapter\"\n // );\n // }\n if (!this.finalAppId) {\n throw new Error(\n \"YUANQI_APP_ID is required, check your env variables or config passed with the adapter\"\n );\n }\n if (!this.yuanqiConfig.appKey && !process.env.YUANQI_APP_KEY) {\n throw new Error(\n \"YUANQI_APP_KEY is required, check your env variables or config passed with the adapter\"\n );\n }\n\n // const tcbClient = tcb.init({\n // env: getCloudbaseEnvId(),\n // });\n\n const openai = this.model as OpenAI;\n\n // Convert AGUI messages to OpenAI format\n const openaiMessages = convertMessagesToOpenAI(messages);\n\n const body = this.generateRequestBody({\n messages: openaiMessages,\n input,\n });\n\n const stream = await openai.chat.completions.create(\n {\n stream: true,\n messages: [],\n model: \"\",\n },\n {\n body: camelToSnakeKeys(body),\n headers: {\n ...this.yuanqiConfig.request?.headers,\n Authorization: `Bearer ${\n this.yuanqiConfig.appKey || process.env.YUANQI_APP_KEY\n }`,\n },\n }\n );\n\n // Process stream and emit AGUI events\n const messageId = `msg_${Date.now()}`;\n const context = { threadId, runId, messageId };\n\n for await (const event of processYuanqiStream(stream, context)) {\n subscriber.next(event);\n }\n } catch (error: any) {\n if (error instanceof Error) {\n console.error(`Error ${error.name}: ${error.message}`);\n } else {\n console.error(JSON.stringify(error));\n }\n subscriber.next({\n type: EventType.RUN_ERROR,\n message: error instanceof Error ? error.message : String(error),\n code: error instanceof Error ? error.name : \"UNKNOWN_ERROR\",\n } as any);\n subscriber.complete();\n }\n }\n}\n\nfunction getCloudbaseEnvId() {\n if (!!process.env.CBR_ENV_ID) {\n return process.env.CBR_ENV_ID;\n } else if (!!process.env.SCF_NAMESPACE) {\n return process.env.SCF_NAMESPACE;\n } else {\n return process.env.CLOUDBASE_ENV_ID || \"\";\n }\n}\n\n/**\n * Convert AGUI messages to OpenAI chat completion format\n */\nexport function convertMessagesToOpenAI(\n messages: Message[],\n systemPrompt?: string\n): OpenAI.Chat.ChatCompletionMessageParam[] {\n const openaiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [];\n\n // Add system prompt if provided\n if (systemPrompt) {\n openaiMessages.push({\n role: \"system\",\n content: systemPrompt,\n });\n }\n\n // Convert messages\n for (const msg of messages) {\n if (msg.role === \"user\") {\n openaiMessages.push({\n role: \"user\",\n content:\n typeof msg.content === \"string\"\n ? msg.content\n : JSON.stringify(msg.content),\n });\n } else if (msg.role === \"assistant\") {\n openaiMessages.push({\n role: \"assistant\",\n content:\n typeof msg.content === \"string\"\n ? msg.content\n : JSON.stringify(msg.content) || \"\",\n tool_calls: msg.toolCalls?.map((tc: any) => ({\n id: tc.id,\n type: \"function\" as const,\n function: {\n name: tc.function.name,\n arguments: tc.function.arguments,\n },\n })),\n });\n } else if (msg.role === \"tool\") {\n openaiMessages.push({\n role: \"tool\",\n tool_call_id: msg.toolCallId!,\n content:\n typeof msg.content === \"string\"\n ? msg.content\n : JSON.stringify(msg.content),\n });\n }\n }\n\n return openaiMessages;\n}\n","/**\n * 将小驼峰字符串转换为下划线格式\n * 例如: \"userName\" -> \"user_name\"\n */\nfunction camelToSnake(str: string): string {\n return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);\n}\n\n/**\n * 递归地将对象的所有小驼峰属性名转换为下划线格式\n */\nexport function camelToSnakeKeys<T>(obj: T): T {\n if (obj === null || obj === undefined) {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item) => camelToSnakeKeys(item)) as T;\n }\n\n if (typeof obj === \"object\") {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n const snakeKey = camelToSnake(key);\n result[snakeKey] = camelToSnakeKeys(value);\n }\n return result as T;\n }\n\n return obj;\n}\n","import OpenAI from \"openai\";\nimport { EventType } from \"@ag-ui/client\";\n\nexport interface StreamContext {\n threadId: string;\n runId: string;\n messageId: string;\n}\n\nexport interface StreamState {\n hasStarted: boolean;\n fullContent: string;\n toolCallsMap: Map<string, { name: string; args: string }>;\n}\n\nexport type Delta =\n OpenAI.Chat.Completions.ChatCompletionChunk[\"choices\"][number][\"delta\"] & {\n reasoning_content?: string;\n };\n\nexport async function* processYuanqiStream(\n stream: AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>,\n context: StreamContext\n) {\n const { threadId, runId, messageId } = context;\n const state: StreamState = {\n hasStarted: false,\n fullContent: \"\",\n toolCallsMap: new Map(),\n };\n const reasoningState: StreamState = {\n hasStarted: false,\n fullContent: \"\",\n toolCallsMap: new Map(),\n };\n\n for await (const chunk of stream) {\n const delta = chunk.choices[0]?.delta as Delta;\n if (!delta) continue;\n\n // Handle text content\n if (delta.content) {\n // End reasoning message if it was started\n if (reasoningState.hasStarted) {\n yield {\n type: EventType.THINKING_TEXT_MESSAGE_END,\n threadId,\n runId,\n messageId,\n };\n yield {\n type: EventType.THINKING_END,\n threadId,\n runId,\n messageId,\n };\n }\n\n if (!state.hasStarted) {\n yield {\n type: EventType.TEXT_MESSAGE_START,\n threadId,\n runId,\n messageId,\n role: \"assistant\",\n };\n state.hasStarted = true;\n }\n\n state.fullContent += delta.content;\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n threadId,\n runId,\n messageId,\n delta: delta.content,\n };\n }\n\n // Handle reasoning content\n if (delta.reasoning_content) {\n if (!reasoningState.hasStarted) {\n yield {\n type: EventType.THINKING_START,\n threadId,\n runId,\n messageId,\n };\n yield {\n type: EventType.THINKING_TEXT_MESSAGE_START,\n threadId,\n runId,\n messageId,\n role: \"assistant\",\n };\n reasoningState.hasStarted = true;\n }\n\n reasoningState.fullContent += delta.reasoning_content;\n yield {\n type: EventType.THINKING_TEXT_MESSAGE_CONTENT,\n threadId,\n runId,\n messageId,\n delta: delta.reasoning_content,\n };\n }\n\n // Handle tool calls\n if (delta.tool_calls) {\n for (const toolCall of delta.tool_calls) {\n const toolCallId = toolCall.id || `tool_${toolCall.index}`;\n\n if (toolCall.function?.name) {\n // Tool call start\n yield {\n type: EventType.TOOL_CALL_START,\n threadId,\n runId,\n toolCallId,\n toolCallName: toolCall.function.name,\n };\n\n state.toolCallsMap.set(toolCallId, {\n name: toolCall.function.name,\n args: toolCall.function.arguments || \"\",\n });\n } else if (toolCall.function?.arguments) {\n // Tool call arguments delta\n const existing = state.toolCallsMap.get(toolCallId);\n if (existing) {\n existing.args += toolCall.function.arguments;\n\n yield {\n type: EventType.TOOL_CALL_ARGS,\n threadId,\n runId,\n toolCallId,\n delta: toolCall.function.arguments,\n };\n }\n }\n }\n }\n }\n\n // Emit TEXT_MESSAGE_END if we had text content\n if (state.hasStarted) {\n yield {\n type: EventType.TEXT_MESSAGE_END,\n threadId,\n runId,\n messageId,\n };\n }\n\n // Emit TOOL_CALL_END for all tool calls\n for (const [toolCallId] of state.toolCallsMap) {\n yield {\n type: EventType.TOOL_CALL_END,\n threadId,\n runId,\n toolCallId,\n };\n }\n\n // Emit RUN_FINISHED\n yield {\n type: EventType.RUN_FINISHED,\n threadId,\n runId,\n };\n}\n"],"mappings":"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","names":["import_client","OpenAI"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
// src/agent.ts
|
|
2
|
+
import {
|
|
3
|
+
AbstractAgent,
|
|
4
|
+
EventType as EventType2
|
|
5
|
+
} from "@ag-ui/client";
|
|
6
|
+
import OpenAI from "openai";
|
|
7
|
+
import { randomUUID } from "crypto";
|
|
8
|
+
|
|
9
|
+
// src/utils.ts
|
|
10
|
+
function camelToSnake(str) {
|
|
11
|
+
return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
12
|
+
}
|
|
13
|
+
function camelToSnakeKeys(obj) {
|
|
14
|
+
if (obj === null || obj === void 0) {
|
|
15
|
+
return obj;
|
|
16
|
+
}
|
|
17
|
+
if (Array.isArray(obj)) {
|
|
18
|
+
return obj.map((item) => camelToSnakeKeys(item));
|
|
19
|
+
}
|
|
20
|
+
if (typeof obj === "object") {
|
|
21
|
+
const result = {};
|
|
22
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
23
|
+
const snakeKey = camelToSnake(key);
|
|
24
|
+
result[snakeKey] = camelToSnakeKeys(value);
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
return obj;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// src/stream.ts
|
|
32
|
+
import { EventType } from "@ag-ui/client";
|
|
33
|
+
async function* processYuanqiStream(stream, context) {
|
|
34
|
+
const { threadId, runId, messageId } = context;
|
|
35
|
+
const state = {
|
|
36
|
+
hasStarted: false,
|
|
37
|
+
fullContent: "",
|
|
38
|
+
toolCallsMap: /* @__PURE__ */ new Map()
|
|
39
|
+
};
|
|
40
|
+
const reasoningState = {
|
|
41
|
+
hasStarted: false,
|
|
42
|
+
fullContent: "",
|
|
43
|
+
toolCallsMap: /* @__PURE__ */ new Map()
|
|
44
|
+
};
|
|
45
|
+
for await (const chunk of stream) {
|
|
46
|
+
const delta = chunk.choices[0]?.delta;
|
|
47
|
+
if (!delta) continue;
|
|
48
|
+
if (delta.content) {
|
|
49
|
+
if (reasoningState.hasStarted) {
|
|
50
|
+
yield {
|
|
51
|
+
type: EventType.THINKING_TEXT_MESSAGE_END,
|
|
52
|
+
threadId,
|
|
53
|
+
runId,
|
|
54
|
+
messageId
|
|
55
|
+
};
|
|
56
|
+
yield {
|
|
57
|
+
type: EventType.THINKING_END,
|
|
58
|
+
threadId,
|
|
59
|
+
runId,
|
|
60
|
+
messageId
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
if (!state.hasStarted) {
|
|
64
|
+
yield {
|
|
65
|
+
type: EventType.TEXT_MESSAGE_START,
|
|
66
|
+
threadId,
|
|
67
|
+
runId,
|
|
68
|
+
messageId,
|
|
69
|
+
role: "assistant"
|
|
70
|
+
};
|
|
71
|
+
state.hasStarted = true;
|
|
72
|
+
}
|
|
73
|
+
state.fullContent += delta.content;
|
|
74
|
+
yield {
|
|
75
|
+
type: EventType.TEXT_MESSAGE_CONTENT,
|
|
76
|
+
threadId,
|
|
77
|
+
runId,
|
|
78
|
+
messageId,
|
|
79
|
+
delta: delta.content
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (delta.reasoning_content) {
|
|
83
|
+
if (!reasoningState.hasStarted) {
|
|
84
|
+
yield {
|
|
85
|
+
type: EventType.THINKING_START,
|
|
86
|
+
threadId,
|
|
87
|
+
runId,
|
|
88
|
+
messageId
|
|
89
|
+
};
|
|
90
|
+
yield {
|
|
91
|
+
type: EventType.THINKING_TEXT_MESSAGE_START,
|
|
92
|
+
threadId,
|
|
93
|
+
runId,
|
|
94
|
+
messageId,
|
|
95
|
+
role: "assistant"
|
|
96
|
+
};
|
|
97
|
+
reasoningState.hasStarted = true;
|
|
98
|
+
}
|
|
99
|
+
reasoningState.fullContent += delta.reasoning_content;
|
|
100
|
+
yield {
|
|
101
|
+
type: EventType.THINKING_TEXT_MESSAGE_CONTENT,
|
|
102
|
+
threadId,
|
|
103
|
+
runId,
|
|
104
|
+
messageId,
|
|
105
|
+
delta: delta.reasoning_content
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
if (delta.tool_calls) {
|
|
109
|
+
for (const toolCall of delta.tool_calls) {
|
|
110
|
+
const toolCallId = toolCall.id || `tool_${toolCall.index}`;
|
|
111
|
+
if (toolCall.function?.name) {
|
|
112
|
+
yield {
|
|
113
|
+
type: EventType.TOOL_CALL_START,
|
|
114
|
+
threadId,
|
|
115
|
+
runId,
|
|
116
|
+
toolCallId,
|
|
117
|
+
toolCallName: toolCall.function.name
|
|
118
|
+
};
|
|
119
|
+
state.toolCallsMap.set(toolCallId, {
|
|
120
|
+
name: toolCall.function.name,
|
|
121
|
+
args: toolCall.function.arguments || ""
|
|
122
|
+
});
|
|
123
|
+
} else if (toolCall.function?.arguments) {
|
|
124
|
+
const existing = state.toolCallsMap.get(toolCallId);
|
|
125
|
+
if (existing) {
|
|
126
|
+
existing.args += toolCall.function.arguments;
|
|
127
|
+
yield {
|
|
128
|
+
type: EventType.TOOL_CALL_ARGS,
|
|
129
|
+
threadId,
|
|
130
|
+
runId,
|
|
131
|
+
toolCallId,
|
|
132
|
+
delta: toolCall.function.arguments
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (state.hasStarted) {
|
|
140
|
+
yield {
|
|
141
|
+
type: EventType.TEXT_MESSAGE_END,
|
|
142
|
+
threadId,
|
|
143
|
+
runId,
|
|
144
|
+
messageId
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
for (const [toolCallId] of state.toolCallsMap) {
|
|
148
|
+
yield {
|
|
149
|
+
type: EventType.TOOL_CALL_END,
|
|
150
|
+
threadId,
|
|
151
|
+
runId,
|
|
152
|
+
toolCallId
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
yield {
|
|
156
|
+
type: EventType.RUN_FINISHED,
|
|
157
|
+
threadId,
|
|
158
|
+
runId
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// src/agent.ts
|
|
163
|
+
import { Observable } from "rxjs";
|
|
164
|
+
var YuanqiAgent = class extends AbstractAgent {
|
|
165
|
+
constructor(config) {
|
|
166
|
+
super(config);
|
|
167
|
+
this.yuanqiConfig = config.yuanqiConfig;
|
|
168
|
+
this.model = new OpenAI({
|
|
169
|
+
apiKey: "",
|
|
170
|
+
baseURL: this.yuanqiConfig.request?.baseUrl || "https://yuanqi.tencent.com/openapi/v1/agent"
|
|
171
|
+
});
|
|
172
|
+
this.finalAppId = this.yuanqiConfig.appId || this.yuanqiConfig.request?.body?.assistantId || process.env.YUANQI_APP_ID || "";
|
|
173
|
+
}
|
|
174
|
+
generateRequestBody({
|
|
175
|
+
messages,
|
|
176
|
+
input
|
|
177
|
+
}) {
|
|
178
|
+
const { state, forwardedProps } = input;
|
|
179
|
+
const requestBody = {
|
|
180
|
+
stream: true,
|
|
181
|
+
...this.yuanqiConfig.request?.body || {},
|
|
182
|
+
...forwardedProps || {},
|
|
183
|
+
assistantId: this.finalAppId,
|
|
184
|
+
userId: state?.__request_context__?.id || forwardedProps?.userId || randomUUID(),
|
|
185
|
+
messages
|
|
186
|
+
};
|
|
187
|
+
return requestBody;
|
|
188
|
+
}
|
|
189
|
+
run(input) {
|
|
190
|
+
return new Observable((subscriber) => {
|
|
191
|
+
this._run(subscriber, input);
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
async _run(subscriber, input) {
|
|
195
|
+
try {
|
|
196
|
+
const { messages, runId, threadId, forwardedProps } = input;
|
|
197
|
+
subscriber.next({
|
|
198
|
+
type: EventType2.RUN_STARTED,
|
|
199
|
+
threadId,
|
|
200
|
+
runId
|
|
201
|
+
});
|
|
202
|
+
if (!this.finalAppId) {
|
|
203
|
+
throw new Error(
|
|
204
|
+
"YUANQI_APP_ID is required, check your env variables or config passed with the adapter"
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
if (!this.yuanqiConfig.appKey && !process.env.YUANQI_APP_KEY) {
|
|
208
|
+
throw new Error(
|
|
209
|
+
"YUANQI_APP_KEY is required, check your env variables or config passed with the adapter"
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
const openai = this.model;
|
|
213
|
+
const openaiMessages = convertMessagesToOpenAI(messages);
|
|
214
|
+
const body = this.generateRequestBody({
|
|
215
|
+
messages: openaiMessages,
|
|
216
|
+
input
|
|
217
|
+
});
|
|
218
|
+
const stream = await openai.chat.completions.create(
|
|
219
|
+
{
|
|
220
|
+
stream: true,
|
|
221
|
+
messages: [],
|
|
222
|
+
model: ""
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
body: camelToSnakeKeys(body),
|
|
226
|
+
headers: {
|
|
227
|
+
...this.yuanqiConfig.request?.headers,
|
|
228
|
+
Authorization: `Bearer ${this.yuanqiConfig.appKey || process.env.YUANQI_APP_KEY}`
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
);
|
|
232
|
+
const messageId = `msg_${Date.now()}`;
|
|
233
|
+
const context = { threadId, runId, messageId };
|
|
234
|
+
for await (const event of processYuanqiStream(stream, context)) {
|
|
235
|
+
subscriber.next(event);
|
|
236
|
+
}
|
|
237
|
+
} catch (error) {
|
|
238
|
+
if (error instanceof Error) {
|
|
239
|
+
console.error(`Error ${error.name}: ${error.message}`);
|
|
240
|
+
} else {
|
|
241
|
+
console.error(JSON.stringify(error));
|
|
242
|
+
}
|
|
243
|
+
subscriber.next({
|
|
244
|
+
type: EventType2.RUN_ERROR,
|
|
245
|
+
message: error instanceof Error ? error.message : String(error),
|
|
246
|
+
code: error instanceof Error ? error.name : "UNKNOWN_ERROR"
|
|
247
|
+
});
|
|
248
|
+
subscriber.complete();
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
function convertMessagesToOpenAI(messages, systemPrompt) {
|
|
253
|
+
const openaiMessages = [];
|
|
254
|
+
if (systemPrompt) {
|
|
255
|
+
openaiMessages.push({
|
|
256
|
+
role: "system",
|
|
257
|
+
content: systemPrompt
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
for (const msg of messages) {
|
|
261
|
+
if (msg.role === "user") {
|
|
262
|
+
openaiMessages.push({
|
|
263
|
+
role: "user",
|
|
264
|
+
content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
|
|
265
|
+
});
|
|
266
|
+
} else if (msg.role === "assistant") {
|
|
267
|
+
openaiMessages.push({
|
|
268
|
+
role: "assistant",
|
|
269
|
+
content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content) || "",
|
|
270
|
+
tool_calls: msg.toolCalls?.map((tc) => ({
|
|
271
|
+
id: tc.id,
|
|
272
|
+
type: "function",
|
|
273
|
+
function: {
|
|
274
|
+
name: tc.function.name,
|
|
275
|
+
arguments: tc.function.arguments
|
|
276
|
+
}
|
|
277
|
+
}))
|
|
278
|
+
});
|
|
279
|
+
} else if (msg.role === "tool") {
|
|
280
|
+
openaiMessages.push({
|
|
281
|
+
role: "tool",
|
|
282
|
+
tool_call_id: msg.toolCallId,
|
|
283
|
+
content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return openaiMessages;
|
|
288
|
+
}
|
|
289
|
+
export {
|
|
290
|
+
YuanqiAgent,
|
|
291
|
+
camelToSnakeKeys,
|
|
292
|
+
convertMessagesToOpenAI
|
|
293
|
+
};
|
|
294
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/agent.ts","../src/utils.ts","../src/stream.ts"],"sourcesContent":["import {\n RunAgentInput,\n Message,\n AbstractAgent,\n AgentConfig,\n BaseEvent,\n EventType,\n} from \"@ag-ui/client\";\n// import tcb from \"@cloudbase/node-sdk\";\nimport OpenAI from \"openai\";\nimport { randomUUID } from \"crypto\";\nimport { camelToSnakeKeys } from \"./utils\";\nimport { processYuanqiStream } from \"./stream\";\nimport { Observable } from \"rxjs\";\n\nexport interface YuanqiConfig {\n request?: {\n baseUrl?: string;\n body?: Partial<YuanqiChatRequest>;\n headers?: Record<string, string>;\n };\n appId?: string;\n appKey?: string;\n}\n\nexport type ChatMessage = OpenAI.Chat.Completions.ChatCompletionMessageParam;\n\nexport interface YuanqiChatRequest {\n assistantId: string;\n userId: string;\n messages: ChatMessage[];\n stream?: boolean;\n customVariables?: Record<string, string>;\n version?: number;\n chatType?: \"published\" | \"preview\";\n [key: string]: any;\n}\n\nexport class YuanqiAgent extends AbstractAgent {\n protected yuanqiConfig: YuanqiConfig;\n private finalAppId: string;\n private model: OpenAI;\n constructor(config: AgentConfig & { yuanqiConfig: YuanqiConfig }) {\n super(config);\n this.yuanqiConfig = config.yuanqiConfig;\n this.model = new OpenAI({\n apiKey: \"\",\n baseURL:\n this.yuanqiConfig.request?.baseUrl ||\n \"https://yuanqi.tencent.com/openapi/v1/agent\",\n });\n this.finalAppId =\n this.yuanqiConfig.appId ||\n this.yuanqiConfig.request?.body?.assistantId ||\n process.env.YUANQI_APP_ID ||\n \"\";\n }\n\n generateRequestBody({\n messages,\n input,\n }: {\n messages: ChatMessage[];\n input: RunAgentInput;\n }): YuanqiChatRequest {\n const { state, forwardedProps } = input;\n const requestBody: YuanqiChatRequest = {\n stream: true,\n ...(this.yuanqiConfig.request?.body || {}),\n ...(forwardedProps || {}),\n\n assistantId: this.finalAppId,\n userId:\n state?.__request_context__?.id ||\n forwardedProps?.userId ||\n randomUUID(),\n messages,\n };\n return requestBody;\n }\n\n run(input: RunAgentInput): Observable<BaseEvent> {\n return new Observable<BaseEvent>((subscriber) => {\n this._run(subscriber, input);\n });\n }\n\n private async _run(subscriber: any, input: RunAgentInput): Promise<void> {\n try {\n const { messages, runId, threadId, forwardedProps } = input;\n\n subscriber.next({\n type: EventType.RUN_STARTED,\n threadId,\n runId,\n });\n\n // if (!getCloudbaseEnvId()) {\n // throw new Error(\n // \"CLOUDBASE_ENV_ID is required, check your env variables or config passed with the adapter\"\n // );\n // }\n if (!this.finalAppId) {\n throw new Error(\n \"YUANQI_APP_ID is required, check your env variables or config passed with the adapter\"\n );\n }\n if (!this.yuanqiConfig.appKey && !process.env.YUANQI_APP_KEY) {\n throw new Error(\n \"YUANQI_APP_KEY is required, check your env variables or config passed with the adapter\"\n );\n }\n\n // const tcbClient = tcb.init({\n // env: getCloudbaseEnvId(),\n // });\n\n const openai = this.model as OpenAI;\n\n // Convert AGUI messages to OpenAI format\n const openaiMessages = convertMessagesToOpenAI(messages);\n\n const body = this.generateRequestBody({\n messages: openaiMessages,\n input,\n });\n\n const stream = await openai.chat.completions.create(\n {\n stream: true,\n messages: [],\n model: \"\",\n },\n {\n body: camelToSnakeKeys(body),\n headers: {\n ...this.yuanqiConfig.request?.headers,\n Authorization: `Bearer ${\n this.yuanqiConfig.appKey || process.env.YUANQI_APP_KEY\n }`,\n },\n }\n );\n\n // Process stream and emit AGUI events\n const messageId = `msg_${Date.now()}`;\n const context = { threadId, runId, messageId };\n\n for await (const event of processYuanqiStream(stream, context)) {\n subscriber.next(event);\n }\n } catch (error: any) {\n if (error instanceof Error) {\n console.error(`Error ${error.name}: ${error.message}`);\n } else {\n console.error(JSON.stringify(error));\n }\n subscriber.next({\n type: EventType.RUN_ERROR,\n message: error instanceof Error ? error.message : String(error),\n code: error instanceof Error ? error.name : \"UNKNOWN_ERROR\",\n } as any);\n subscriber.complete();\n }\n }\n}\n\nfunction getCloudbaseEnvId() {\n if (!!process.env.CBR_ENV_ID) {\n return process.env.CBR_ENV_ID;\n } else if (!!process.env.SCF_NAMESPACE) {\n return process.env.SCF_NAMESPACE;\n } else {\n return process.env.CLOUDBASE_ENV_ID || \"\";\n }\n}\n\n/**\n * Convert AGUI messages to OpenAI chat completion format\n */\nexport function convertMessagesToOpenAI(\n messages: Message[],\n systemPrompt?: string\n): OpenAI.Chat.ChatCompletionMessageParam[] {\n const openaiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [];\n\n // Add system prompt if provided\n if (systemPrompt) {\n openaiMessages.push({\n role: \"system\",\n content: systemPrompt,\n });\n }\n\n // Convert messages\n for (const msg of messages) {\n if (msg.role === \"user\") {\n openaiMessages.push({\n role: \"user\",\n content:\n typeof msg.content === \"string\"\n ? msg.content\n : JSON.stringify(msg.content),\n });\n } else if (msg.role === \"assistant\") {\n openaiMessages.push({\n role: \"assistant\",\n content:\n typeof msg.content === \"string\"\n ? msg.content\n : JSON.stringify(msg.content) || \"\",\n tool_calls: msg.toolCalls?.map((tc: any) => ({\n id: tc.id,\n type: \"function\" as const,\n function: {\n name: tc.function.name,\n arguments: tc.function.arguments,\n },\n })),\n });\n } else if (msg.role === \"tool\") {\n openaiMessages.push({\n role: \"tool\",\n tool_call_id: msg.toolCallId!,\n content:\n typeof msg.content === \"string\"\n ? msg.content\n : JSON.stringify(msg.content),\n });\n }\n }\n\n return openaiMessages;\n}\n","/**\n * 将小驼峰字符串转换为下划线格式\n * 例如: \"userName\" -> \"user_name\"\n */\nfunction camelToSnake(str: string): string {\n return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);\n}\n\n/**\n * 递归地将对象的所有小驼峰属性名转换为下划线格式\n */\nexport function camelToSnakeKeys<T>(obj: T): T {\n if (obj === null || obj === undefined) {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map((item) => camelToSnakeKeys(item)) as T;\n }\n\n if (typeof obj === \"object\") {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n const snakeKey = camelToSnake(key);\n result[snakeKey] = camelToSnakeKeys(value);\n }\n return result as T;\n }\n\n return obj;\n}\n","import OpenAI from \"openai\";\nimport { EventType } from \"@ag-ui/client\";\n\nexport interface StreamContext {\n threadId: string;\n runId: string;\n messageId: string;\n}\n\nexport interface StreamState {\n hasStarted: boolean;\n fullContent: string;\n toolCallsMap: Map<string, { name: string; args: string }>;\n}\n\nexport type Delta =\n OpenAI.Chat.Completions.ChatCompletionChunk[\"choices\"][number][\"delta\"] & {\n reasoning_content?: string;\n };\n\nexport async function* processYuanqiStream(\n stream: AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>,\n context: StreamContext\n) {\n const { threadId, runId, messageId } = context;\n const state: StreamState = {\n hasStarted: false,\n fullContent: \"\",\n toolCallsMap: new Map(),\n };\n const reasoningState: StreamState = {\n hasStarted: false,\n fullContent: \"\",\n toolCallsMap: new Map(),\n };\n\n for await (const chunk of stream) {\n const delta = chunk.choices[0]?.delta as Delta;\n if (!delta) continue;\n\n // Handle text content\n if (delta.content) {\n // End reasoning message if it was started\n if (reasoningState.hasStarted) {\n yield {\n type: EventType.THINKING_TEXT_MESSAGE_END,\n threadId,\n runId,\n messageId,\n };\n yield {\n type: EventType.THINKING_END,\n threadId,\n runId,\n messageId,\n };\n }\n\n if (!state.hasStarted) {\n yield {\n type: EventType.TEXT_MESSAGE_START,\n threadId,\n runId,\n messageId,\n role: \"assistant\",\n };\n state.hasStarted = true;\n }\n\n state.fullContent += delta.content;\n yield {\n type: EventType.TEXT_MESSAGE_CONTENT,\n threadId,\n runId,\n messageId,\n delta: delta.content,\n };\n }\n\n // Handle reasoning content\n if (delta.reasoning_content) {\n if (!reasoningState.hasStarted) {\n yield {\n type: EventType.THINKING_START,\n threadId,\n runId,\n messageId,\n };\n yield {\n type: EventType.THINKING_TEXT_MESSAGE_START,\n threadId,\n runId,\n messageId,\n role: \"assistant\",\n };\n reasoningState.hasStarted = true;\n }\n\n reasoningState.fullContent += delta.reasoning_content;\n yield {\n type: EventType.THINKING_TEXT_MESSAGE_CONTENT,\n threadId,\n runId,\n messageId,\n delta: delta.reasoning_content,\n };\n }\n\n // Handle tool calls\n if (delta.tool_calls) {\n for (const toolCall of delta.tool_calls) {\n const toolCallId = toolCall.id || `tool_${toolCall.index}`;\n\n if (toolCall.function?.name) {\n // Tool call start\n yield {\n type: EventType.TOOL_CALL_START,\n threadId,\n runId,\n toolCallId,\n toolCallName: toolCall.function.name,\n };\n\n state.toolCallsMap.set(toolCallId, {\n name: toolCall.function.name,\n args: toolCall.function.arguments || \"\",\n });\n } else if (toolCall.function?.arguments) {\n // Tool call arguments delta\n const existing = state.toolCallsMap.get(toolCallId);\n if (existing) {\n existing.args += toolCall.function.arguments;\n\n yield {\n type: EventType.TOOL_CALL_ARGS,\n threadId,\n runId,\n toolCallId,\n delta: toolCall.function.arguments,\n };\n }\n }\n }\n }\n }\n\n // Emit TEXT_MESSAGE_END if we had text content\n if (state.hasStarted) {\n yield {\n type: EventType.TEXT_MESSAGE_END,\n threadId,\n runId,\n messageId,\n };\n }\n\n // Emit TOOL_CALL_END for all tool calls\n for (const [toolCallId] of state.toolCallsMap) {\n yield {\n type: EventType.TOOL_CALL_END,\n threadId,\n runId,\n toolCallId,\n };\n }\n\n // Emit RUN_FINISHED\n yield {\n type: EventType.RUN_FINISHED,\n threadId,\n runId,\n };\n}\n"],"mappings":"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","names":["EventType","EventType"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cloudbase/agent-adapter-yuanqi",
|
|
3
|
+
"version": "1.0.1-alpha.12",
|
|
4
|
+
"description": "Tencent Yuanqi adapter for AG-Kit agents",
|
|
5
|
+
"files": [
|
|
6
|
+
"dist/",
|
|
7
|
+
"README.md",
|
|
8
|
+
"CHANGELOG.md"
|
|
9
|
+
],
|
|
10
|
+
"main": "dist/index.js",
|
|
11
|
+
"module": "dist/index.mjs",
|
|
12
|
+
"types": "dist/index.d.ts",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"import": {
|
|
16
|
+
"types": "./dist/index.d.mts",
|
|
17
|
+
"default": "./dist/index.mjs"
|
|
18
|
+
},
|
|
19
|
+
"require": {
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"default": "./dist/index.js"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"ag-kit",
|
|
27
|
+
"yuanqi",
|
|
28
|
+
"adapter",
|
|
29
|
+
"agent"
|
|
30
|
+
],
|
|
31
|
+
"author": "",
|
|
32
|
+
"license": "ISC",
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/node": "^20.0.0",
|
|
35
|
+
"tsup": "^8.5.0",
|
|
36
|
+
"typescript": "^5.0.0"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@ag-ui/client": "0.0.42",
|
|
40
|
+
"@cloudbase/node-sdk": "^3.14.2",
|
|
41
|
+
"openai": "^4.0.0",
|
|
42
|
+
"rxjs": "^7.8.1"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"openai": "^4.0.0"
|
|
46
|
+
},
|
|
47
|
+
"peerDependenciesMeta": {
|
|
48
|
+
"openai": {
|
|
49
|
+
"optional": true
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
54
|
+
"build": "tsup --config tsup.config.ts"
|
|
55
|
+
}
|
|
56
|
+
}
|