Skip to content

Commit 3e12044

Browse files
committed
feat(ai): integrate new AI SDK and refactor AI services
- Replaced @langchain/openai with @ai-sdk/openai for improved functionality. - Updated AI service methods to utilize the new SDK, including getOpenAiProvider and getOpenAiModel. - Refactored AI agent and deep reading services to implement new tool definitions and execution logic. - Enhanced comment service to utilize the new AI model for content review and spam detection. - Added new AI prompts for summarization and title generation. Signed-off-by: Innei <[email protected]>
1 parent a463551 commit 3e12044

File tree

13 files changed

+1344
-1174
lines changed

13 files changed

+1344
-1174
lines changed

apps/core/package.json

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
"changelog": true
4949
},
5050
"dependencies": {
51+
"@ai-sdk/openai": "1.3.23",
5152
"@algolia/client-search": "^4.22.1",
5253
"@babel/core": "7.27.4",
5354
"@babel/plugin-transform-modules-commonjs": "7.27.1",
@@ -59,7 +60,6 @@
5960
"@innei/next-async": "0.3.0",
6061
"@innei/pretty-logger-nestjs": "0.3.4",
6162
"@keyv/redis": "4.4.0",
62-
"@langchain/openai": "0.5.10",
6363
"@mx-space/compiled": "workspace:*",
6464
"@nestjs/cache-manager": "3.0.1",
6565
"@nestjs/common": "11.1.3",
@@ -77,6 +77,7 @@
7777
"@typegoose/auto-increment": "4.13.0",
7878
"@typegoose/typegoose": "12.16.0",
7979
"@types/jsonwebtoken": "9.0.9",
80+
"ai": "4.3.17",
8081
"algoliasearch": "4.24.0",
8182
"axios": "^1.9.0",
8283
"axios-retry": "4.5.0",
@@ -98,7 +99,6 @@
9899
"jsonwebtoken": "9.0.2",
99100
"jszip": "3.10.1",
100101
"keyv": "5.3.4",
101-
"langchain": "0.3.25",
102102
"linkedom": "0.18.11",
103103
"lodash": "^4.17.21",
104104
"lru-cache": "11.1.0",
@@ -131,8 +131,6 @@
131131
"zx-cjs": "7.0.7-0"
132132
},
133133
"devDependencies": {
134-
"@langchain/core": "0.3.55",
135-
"@langchain/langgraph": "0.2.72",
136134
"@nestjs/cli": "11.0.7",
137135
"@nestjs/schematics": "11.0.5",
138136
"@nestjs/testing": "11.1.3",
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
# AI Agent API Documentation
2+
3+
## Overview
4+
The AI Agent module provides a chat bot API that can answer questions about your blog content using AI. It supports both streaming and synchronous responses.
5+
6+
## Configuration
7+
To enable the AI Agent, you need to configure the following in the admin panel:
8+
9+
1. Go to Settings > AI Settings
10+
2. Set `openAiKey` - Your OpenAI API key
11+
3. Set `openAiEndpoint` (optional) - Custom OpenAI endpoint if needed
12+
4. Set `openAiPreferredModel` - Default: `gpt-4o-mini`
13+
5. Enable `enableAIAgent` - Toggle to enable the AI Agent feature
14+
15+
## API Endpoints
16+
17+
### POST `/api/ai/agent/chat` (Streaming)
18+
Chat with the AI agent about your blog content with streaming responses.
19+
20+
**Authentication**: Required (must be logged in)
21+
22+
**Request Body**:
23+
```json
24+
{
25+
"message": "What are your latest blog posts?",
26+
"context": [ // Optional conversation history
27+
{
28+
"role": "user",
29+
"content": "Previous question"
30+
},
31+
{
32+
"role": "assistant",
33+
"content": "Previous answer"
34+
}
35+
]
36+
}
37+
```
38+
39+
**Response**: Server-Sent Events (SSE) stream
40+
```
41+
data: {"type":"text","text":"Here are the latest blog posts"}
42+
data: {"type":"text","text":"..."}
43+
data: {"type":"finish","finishReason":"stop"}
44+
```
45+
46+
### POST `/api/ai/agent/chat/sync` (Non-Streaming)
47+
Chat with the AI agent synchronously (waits for complete response).
48+
49+
**Authentication**: Required
50+
51+
**Request Body**: Same as streaming endpoint
52+
53+
**Response**:
54+
```json
55+
{
56+
"message": "Here are the latest blog posts...",
57+
"timestamp": "2024-01-01T12:00:00.000Z"
58+
}
59+
```
60+
61+
### GET `/api/ai/agent/status`
62+
Check if the AI Agent is enabled and configured.
63+
64+
**Authentication**: Required
65+
66+
**Response**:
67+
```json
68+
{
69+
"enabled": true,
70+
"service": "AI Agent Chat"
71+
}
72+
```
73+
74+
## Available Tools
75+
The AI Agent has access to the following tools to query your blog data:
76+
77+
- **Posts**: Get posts by ID, list posts, get latest post, get posts by category/tag
78+
- **Notes**: Get notes by ID, list notes, get latest notes
79+
- **Categories**: Get category info, list all categories
80+
- **Tags**: Get tag summary, get posts by tag
81+
- **Pages**: Get page by ID, list all pages
82+
- **Says**: Get all says, get random say
83+
- **Recently**: Get recent activities
84+
- **Comments**: Get comments, get comments by content
85+
86+
## Example Usage
87+
88+
### Streaming Chat (JavaScript/TypeScript)
89+
```javascript
90+
const response = await fetch('http://localhost:2333/api/ai/agent/chat', {
91+
method: 'POST',
92+
headers: {
93+
'Authorization': 'Bearer YOUR_TOKEN',
94+
'Content-Type': 'application/json'
95+
},
96+
body: JSON.stringify({
97+
message: 'What blog posts do you have about TypeScript?'
98+
})
99+
})
100+
101+
const reader = response.body.getReader()
102+
const decoder = new TextDecoder()
103+
104+
while (true) {
105+
const { done, value } = await reader.read()
106+
if (done) break
107+
108+
const text = decoder.decode(value)
109+
const lines = text.split('\n')
110+
111+
for (const line of lines) {
112+
if (line.startsWith('data: ')) {
113+
const data = JSON.parse(line.slice(6))
114+
if (data.type === 'text') {
115+
console.log(data.text)
116+
}
117+
}
118+
}
119+
}
120+
```
121+
122+
### Synchronous Chat
123+
```bash
124+
curl -X POST http://localhost:2333/api/ai/agent/chat/sync \
125+
-H "Authorization: Bearer YOUR_TOKEN" \
126+
-H "Content-Type: application/json" \
127+
-d '{
128+
"message": "What blog posts do you have about TypeScript?"
129+
}'
130+
```
131+
132+
### Check AI Agent Status
133+
```bash
134+
curl -X GET http://localhost:2333/api/ai/agent/status \
135+
-H "Authorization: Bearer YOUR_TOKEN"
136+
```
137+
138+
## Streaming Response Format
139+
The streaming endpoint uses Server-Sent Events (SSE) format. Each message is prefixed with `data: ` and contains a JSON object:
140+
141+
- `{"type":"text","text":"..."}` - Partial text response
142+
- `{"type":"tool-call","toolCall":{...}}` - Tool invocation information
143+
- `{"type":"tool-result","toolResult":{...}}` - Tool execution result
144+
- `{"type":"finish","finishReason":"stop"}` - Stream completion
145+
146+
## Error Codes
147+
- `ErrorCodeEnum.AINotEnabled` - AI Agent is not enabled in the configuration
148+
- Authentication errors - User must be logged in to use the AI Agent
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { Readable } from 'node:stream'
2+
import { FastifyReply } from 'fastify'
3+
4+
import { Body, Get, Post, Res } from '@nestjs/common'
5+
6+
import { ApiController } from '~/common/decorators/api-controller.decorator'
7+
import { Auth } from '~/common/decorators/auth.decorator'
8+
import { BizException } from '~/common/exceptions/biz.exception'
9+
import { ErrorCodeEnum } from '~/constants/error-code.constant'
10+
11+
import { ConfigsService } from '../../configs/configs.service'
12+
import { ChatRequestDto, ChatResponseDto } from './ai-agent.dto'
13+
import { AIAgentService } from './ai-agent.service'
14+
15+
@ApiController('ai/agent')
16+
export class AIAgentController {
17+
constructor(
18+
private readonly aiAgentService: AIAgentService,
19+
private readonly configService: ConfigsService,
20+
) {}
21+
22+
@Post('chat')
23+
@Auth()
24+
async chat(
25+
@Body() chatRequest: ChatRequestDto,
26+
@Res() reply: FastifyReply,
27+
): Promise<void> {
28+
const result = await this.aiAgentService.chatWithTools(chatRequest.message)
29+
30+
// 使用 toDataStreamResponse 获取 SSE 格式的响应
31+
const response = result.toDataStreamResponse()
32+
33+
// 设置响应头
34+
response.headers.forEach((value, key) => {
35+
reply.header(key, value)
36+
})
37+
38+
// 将 Web Response 的 ReadableStream 转换为 Node.js Readable 流
39+
if (response.body) {
40+
const nodeStream = Readable.fromWeb(response.body as any)
41+
return reply.send(nodeStream)
42+
}
43+
}
44+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { IsArray, IsNotEmpty, IsOptional, IsString } from 'class-validator'
2+
3+
export class ChatRequestDto {
4+
@IsString()
5+
@IsNotEmpty()
6+
message: string
7+
8+
@IsArray()
9+
@IsOptional()
10+
context?: Array<{
11+
role: 'user' | 'assistant'
12+
content: string
13+
}>
14+
}
15+
16+
export class ChatResponseDto {
17+
message: string
18+
timestamp: Date
19+
}

0 commit comments

Comments
 (0)