Skip to content

Commit

Permalink
📝 docs: core api request docs (#282)
Browse files Browse the repository at this point in the history
  • Loading branch information
ONLY-yours authored Jul 25, 2024
1 parent 4b5c1c6 commit f45516d
Show file tree
Hide file tree
Showing 5 changed files with 211 additions and 4 deletions.
104 changes: 104 additions & 0 deletions docs/guide/request-api-intro.en-US.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
---
title: Detailed Explanation of API Core Request
group:
title: Use Cases
order: 10
nav:
title: Documentation
order: 0
---

# Detailed Explanation of API Core Request

First and foremost, it's important to understand that the core of ProChat is actually the data maintenance scheme. We follow the Data-Modal logic for rendering UI.

Therefore, you will also find that we have configured `chatItemRenderConfig` to help you render each chat unit in ProChat.

### Simple Usage

For simple conversation methods, basic String rendering is sufficient. This is how we initially designed it in the Request.

```js
<ProChat
request={async (messages) => {
const text = await delay(
`This is a simulated non-streaming message output. This session received ${messages.length} messages`,
);
return new Response(text);
}}
/>
```

Since we need to design compatibility with both SSE and regular output simultaneously, we require users to return the Response from this request back to us.

This raises a question: What if users want to return additional information for a particular data stream? For example, if they want to control parameters like id or createTime? Or even role?

> These parameters are automatically generated by ProChat by default; role defaults to user and assistant.
Although we've provided some hooks like `setMessageContent` that allow programmatic content modification, it's obviously not ideal for users to manually change them every time.

### Advanced Structure

We have designed an extendable structure:

```ts
export type MixRequestResponse = Response | { content?: Response; [key: string]: any } | string;
```

Now you can use it like this:

```js
<ProChat
request={async (messages) => {
const text = await delay(
`This is a simulated non-streaming message output. This session received ${messages.length} messages`,
);
return {
content: new Response(text),
id: 'only-you-love-me',
role: 'user-King',
keys: ['Ovo'],
};
}}
/>
```

We will read your object and place its Content into the data stream as usual; other structures will also be included as-is. There are two scenarios here:

- If the key-value pairs inserted are those originally intended by the framework (e.g., id, createAt, role), except for id which ensures unique search capabilities (you can use genMessageId), others will replace what was supposed to be generated.
- If the inserted keys do not belong to pre-generated ones, they will be added additionally.

You'll find that except for id, other contents are additionally passed in.

> You might notice an extra Meta field in OriginData; this is an old version compatibility scheme where both outer layer and Meta data are essentially identical. You can choose either one.
![](https://mdn.alipayobjects.com/huamei_re70wt/afts/img/A*QX7BR6hI6FYAAAAAAAAAAAAADmuEAQ/original)

### Signal Control & Additional Parameters

Besides messages as conversation context, there are two more parameters in request input:

- extra: Additional parameter content configurable via config. Params inside config serve as additional fields while others are general modal parameters which you may or may not use—typically used with model switching or model parameter panels.
- signal: Network request control such as SSE passed into Http requests or fetch/request etc., allowing network disconnection upon stopping generation (prevents situations where frontend stops generating but interface remains connected).

```js
<ProChat
config={{
params: {
userId: '123',
extra: 'extra',
},
}}
request={async (messages, extra, signal) => {
const text = await delay(
`This is a simulated non-streaming message output. This session received ${messages.length} messages`,
);
return {
content: new Response(text),
id: 'only-you-love-me',
role: 'user-King',
keys: ['Ovo'],
};
}}
/>
```
104 changes: 104 additions & 0 deletions docs/guide/request-api-intro.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
---
title: 详解 api 核心 Request
group:
title: 使用案例
order: 10
nav:
title: 文档
order: 0
---

# 详解 api 核心 Request

首先要清楚,ProChat 中最核心的其实是数据维护方案。我们遵循 Data-Modal 数据渲染 UI 这一套逻辑

所以你也会发现,我们配置了 chatItemRenderConfig 来帮你完成 ProChat 中各个对话单元的渲染。

### 简单用法

对于简单的对话方式来说,最基本的 String 渲染即可,我们最开始在 Request 就是这么设计的。

```js
<ProChat
request={async (messages) => {
const text = await delay(
`这是一条模拟非流式输出的消息的消息。本次会话传入了${messages.length}条消息`,
);
return new Response(text);
}}
/>
```

因为要设计同时兼容 SSE 和普通的输出,我们渲染让用户把这条请求的 Response 返回给我们。

这样子就会遇到一个问题?如果用户要给某个数据流返回额外的信息呢?例如,他们想要控制 id、createTime 这些内容?甚至是 role(角色)

> 这几个参数都会在 ProChat 由程序默认帮你生成,role 默认是 user 和 assistant
这个时候我们虽然提供了一些 Hooks,例如 setMessageContent 可以帮你程序化的修改内容,难道用户还要每次自己手动改一改么?这显然不太好。

### 高级结构

我们设计了一个可以拓展的结构

```ts
export type MixRequestResponse = Response | { content?: Response; [key: string]: any } | string;
```

现在你可以这么使用它

```js
<ProChat
request={async (messages) => {
const text = await delay(
`这是一条模拟非流式输出的消息的消息。本次会话传入了${messages.length}条消息`,
);
return {
content: new Response(text),
id: 'only-you-love-me',
role: 'user-King',
keys: ['Ovo'],
};
}}
/>
```

我们会读取你的对象,对象里面的 Content 我们依然照常给你放到数据流中,其他的结构我们则会一样给你塞进去,这里面就分为两种情况了。

- 如果塞入的键值对是框架原本打算给你生成的,例如 id、createAt、role 这一类,除了 id 这个字段外,其余的我们会使用你传入的来替换(id 是用于关键的搜索能力,我们会帮你保持唯一,或者请你使用 genMessageId 来写)
- 如果传入的不属于预生成的,我们也会额外塞入进去。

你会发现除了 id 外 ,其余的内容会额外传入进入。

> 你可能会注意 OriginData 里面还有一个 Meta 字段,这个是之前的老版本兼容方案,本质上外层和 Meta 数据是一致的,你选择哪一种都可以。
![](https://mdn.alipayobjects.com/huamei_re70wt/afts/img/A*QX7BR6hI6FYAAAAAAAAAAAAADmuEAQ/original)

### Signal 控制 & 额外参数

除此之外,request 入参除开第一个 messages 作为对话上下文外,还有两个参数

- extra: 额外参数内容,你可以通过 config 来进行配置,config 里面的 params 是一个额外字段,其他都是通用的 modal 参数,你可以选择使用,也可以不使用。一般会配合一些切换模型、模型参数面板等一起用
- signal: SSE 等网络请求控制,把这个传给 Http 请求,或者 fetch、request 等,这样子当你点击停止生成的时候,就会把网络请求给断开。(否则会出现明明点击了停止生成,前端不在继续生成了,但是接口没有断开的情况)

```js
<ProChat
config={{
params: {
userId: '123',
extra: 'extra',
},
}}
request={async (messages, extra, signal) => {
const text = await delay(
`这是一条模拟非流式输出的消息的消息。本次会话传入了${messages.length}条消息`,
);
return {
content: new Response(text),
id: 'only-you-love-me',
role: 'user-King',
keys: ['Ovo'],
};
}}
/>
```
2 changes: 1 addition & 1 deletion docs/guide/storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: 使用浏览器缓存
group:
title: 使用案例
order: 10
order: 11
nav:
title: 文档
order: 0
Expand Down
2 changes: 0 additions & 2 deletions src/ProChat/store/action.ts
Original file line number Diff line number Diff line change
Expand Up @@ -333,8 +333,6 @@ export const chatAction: StateCreator<ChatStore, [['zustand/devtools', never]],
// 因为如果顺序反了,messages 中将包含新增的 ai message
const mid = await getMessageId(messages, userMessageId);

console.log('fetch Real');

dispatchMessage({
id: mid,
message: LOADING_FLAT,
Expand Down
3 changes: 2 additions & 1 deletion src/ProChat/store/reducers/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,15 @@ export const messagesReducer = (
const message = draftState.find((m) => m.id === id);
if (!message) return;

message.updateAt = Date.now();

// 遍历 rest 对象并更新 message 对象
for (const [restKey, restValue] of Object.entries(rest)) {
message[restKey] = restValue;
}

// @ts-ignore
message[key] = value;
message.updateAt = Date.now();
});
}

Expand Down

0 comments on commit f45516d

Please sign in to comment.