misc: add stream answer for AI response

This commit is contained in:
WJG 2024-02-24 20:33:35 +08:00
parent 631876d2ac
commit db9adb652d
No known key found for this signature in database
GPG Key ID: 258474EF8590014A
5 changed files with 27 additions and 19 deletions

View File

@ -4,10 +4,11 @@ import {
SpeakerCommand, SpeakerCommand,
SpeakerConfig, SpeakerConfig,
QueryMessage, QueryMessage,
SpeakerAnswer,
} from "./speaker"; } from "./speaker";
export type AISpeakerConfig = SpeakerConfig & { export type AISpeakerConfig = SpeakerConfig & {
askAI?: (msg: QueryMessage) => Promise<string>; askAI?: (msg: QueryMessage) => Promise<SpeakerAnswer>;
/** /**
* *
* *
@ -200,7 +201,7 @@ export class AISpeaker extends Speaker {
]; ];
async askAIForAnswer(msg: QueryMessage) { async askAIForAnswer(msg: QueryMessage) {
let data: any = {}; let data: { answer?: SpeakerAnswer } = {};
const { hasNewMsg } = this.checkIfHasNewMsg(msg); const { hasNewMsg } = this.checkIfHasNewMsg(msg);
for (const action of this._askAIForAnswerSteps) { for (const action of this._askAIForAnswerSteps) {
const res = await action(msg, data); const res = await action(msg, data);

View File

@ -8,7 +8,7 @@ import {
} from "mi-service-lite"; } from "mi-service-lite";
import { sleep } from "../../utils/base"; import { sleep } from "../../utils/base";
import { Http } from "../http"; import { Http } from "../http";
import { ResponseStream } from "./stream"; import { StreamResponse } from "./stream";
export type TTSProvider = "xiaoai" | "doubao"; export type TTSProvider = "xiaoai" | "doubao";
@ -59,7 +59,7 @@ export class BaseSpeaker {
async response(options: { async response(options: {
tts?: TTSProvider; tts?: TTSProvider;
text?: string; text?: string;
stream?: ResponseStream; stream?: StreamResponse;
audio?: string; audio?: string;
speaker?: string; speaker?: string;
keepAlive?: boolean; keepAlive?: boolean;
@ -79,7 +79,7 @@ export class BaseSpeaker {
if (ttsNotXiaoai && !stream) { if (ttsNotXiaoai && !stream) {
// 长文本 TTS 转化成 stream 分段模式 // 长文本 TTS 转化成 stream 分段模式
stream = ResponseStream.createResponseStream(text!); stream = StreamResponse.createStreamResponse(text!);
} }
let res; let res;
@ -139,7 +139,7 @@ export class BaseSpeaker {
private async _response(options: { private async _response(options: {
tts?: TTSProvider; tts?: TTSProvider;
text?: string; text?: string;
stream?: ResponseStream; stream?: StreamResponse;
audio?: string; audio?: string;
speaker?: string; speaker?: string;
keepAlive?: boolean; keepAlive?: boolean;

View File

@ -1,5 +1,6 @@
import { firstOf, lastOf, sleep } from "../../utils/base"; import { firstOf, lastOf, sleep } from "../../utils/base";
import { BaseSpeaker, BaseSpeakerConfig } from "./base"; import { BaseSpeaker, BaseSpeakerConfig } from "./base";
import { StreamResponse } from "./stream";
export interface QueryMessage { export interface QueryMessage {
text: string; text: string;
@ -10,12 +11,18 @@ export interface QueryMessage {
timestamp: number; timestamp: number;
} }
export interface SpeakerAnswer {
text?: string;
url?: string;
steam?: StreamResponse;
}
export interface SpeakerCommand { export interface SpeakerCommand {
match: (msg: QueryMessage) => boolean; match: (msg: QueryMessage) => boolean;
/** /**
* *
*/ */
run: (msg: QueryMessage) => Promise<string | undefined | void>; run: (msg: QueryMessage) => Promise<SpeakerAnswer | undefined | void>;
} }
export type SpeakerConfig = BaseSpeakerConfig & { export type SpeakerConfig = BaseSpeakerConfig & {
@ -105,7 +112,7 @@ export class Speaker extends BaseSpeaker {
if (answer) { if (answer) {
if (noNewMsg()) { if (noNewMsg()) {
await this.response({ await this.response({
text: answer, ...answer,
keepAlive: this.keepAlive, keepAlive: this.keepAlive,
}); });
} }

View File

@ -1,6 +1,6 @@
type ResponseStatus = "idle" | "responding" | "finished" | "canceled"; type ResponseStatus = "idle" | "responding" | "finished" | "canceled";
interface ResponseStreamOptions { interface StreamResponseOptions {
/** /**
* *
*/ */
@ -23,12 +23,12 @@ interface ResponseStreamOptions {
batchSubmitTimeout?: number; batchSubmitTimeout?: number;
} }
export class ResponseStream { export class StreamResponse {
// 将已有的大篇文字回复 chuck 成 stream 回复 // 将已有的大篇文字回复 chuck 成 stream 回复
static createResponseStream(text: string, options?: ResponseStreamOptions) { static createStreamResponse(text: string, options?: StreamResponseOptions) {
const { maxSentenceLength = 100 } = options ?? {}; const { maxSentenceLength = 100 } = options ?? {};
if (text.length > maxSentenceLength) { if (text.length > maxSentenceLength) {
const stream = new ResponseStream(options); const stream = new StreamResponse(options);
stream.addResponse(text); stream.addResponse(text);
stream.finish(); stream.finish();
return stream; return stream;
@ -38,7 +38,7 @@ export class ResponseStream {
maxSentenceLength: number; maxSentenceLength: number;
firstSubmitTimeout: number; firstSubmitTimeout: number;
batchSubmitTimeout: number; batchSubmitTimeout: number;
constructor(options?: ResponseStreamOptions) { constructor(options?: StreamResponseOptions) {
const { const {
maxSentenceLength = 100, maxSentenceLength = 100,
firstSubmitTimeout = 200, firstSubmitTimeout = 200,
@ -173,7 +173,7 @@ export class ResponseStream {
} }
} }
const stream = new ResponseStream(); const stream = new StreamResponse();
// ai onNewText // ai onNewText
// { // {

View File

@ -1,5 +1,5 @@
import { AISpeaker } from "../src/services/speaker/ai"; import { AISpeaker } from "../src/services/speaker/ai";
import { ResponseStream } from "../src/services/speaker/stream"; import { StreamResponse } from "../src/services/speaker/stream";
import { sleep } from "../src/utils/base"; import { sleep } from "../src/utils/base";
export async function testSpeaker() { export async function testSpeaker() {
@ -13,16 +13,16 @@ export async function testSpeaker() {
const speaker = new AISpeaker(config); const speaker = new AISpeaker(config);
await speaker.initMiServices(); await speaker.initMiServices();
// await testSpeakerResponse(speaker); // await testSpeakerResponse(speaker);
await testSpeakerStreamResponse(speaker); // await testSpeakerStreamResponse(speaker);
// await testSpeakerGetMessages(speaker); // await testSpeakerGetMessages(speaker);
// await testSwitchSpeaker(speaker); // await testSwitchSpeaker(speaker);
// await testSpeakerUnWakeUp(speaker); // await testSpeakerUnWakeUp(speaker);
// await testAISpeaker(speaker); await testAISpeaker(speaker);
} }
async function testAISpeaker(speaker: AISpeaker) { async function testAISpeaker(speaker: AISpeaker) {
speaker.askAI = async (msg) => { speaker.askAI = async (msg) => {
return "你说:" + msg.text; return { text: "你说:" + msg.text };
}; };
await speaker.run(); await speaker.run();
console.log("finished"); console.log("finished");
@ -60,7 +60,7 @@ async function testSpeakerResponse(speaker: AISpeaker) {
} }
async function testSpeakerStreamResponse(speaker: AISpeaker) { async function testSpeakerStreamResponse(speaker: AISpeaker) {
const stream = new ResponseStream(); const stream = new StreamResponse();
const add = async (text: string) => { const add = async (text: string) => {
stream.addResponse(text); stream.addResponse(text);
await sleep(100); await sleep(100);