mirror of
https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
synced 2025-05-25 23:20:19 +09:00
fix: 修复问题
This commit is contained in:
parent
e3d3eed74b
commit
a5bc735548
@ -18,10 +18,7 @@ export class DuckDuckGo extends Tool {
|
|||||||
|
|
||||||
const results = searchResults.results
|
const results = searchResults.results
|
||||||
.slice(0, this.maxResults)
|
.slice(0, this.maxResults)
|
||||||
.map(
|
.map(({ title, description, url }) => htmlToText(description))
|
||||||
({ title, description, url }) =>
|
|
||||||
`title:${title}\ncontent:${htmlToText(description)}\nurl:${url}`,
|
|
||||||
)
|
|
||||||
.join("\n\n");
|
.join("\n\n");
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
|
70
app/api/langchain-tools/http_get.ts
Normal file
70
app/api/langchain-tools/http_get.ts
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
import { htmlToText } from "html-to-text";
|
||||||
|
import { Tool } from "langchain/tools";
|
||||||
|
|
||||||
|
export interface Headers {
|
||||||
|
[key: string]: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RequestTool {
|
||||||
|
headers: Headers;
|
||||||
|
maxOutputLength?: number;
|
||||||
|
timeout: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class HttpGetTool extends Tool implements RequestTool {
|
||||||
|
name = "http_get";
|
||||||
|
|
||||||
|
maxOutputLength = Infinity;
|
||||||
|
|
||||||
|
timeout = 10000;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public headers: Headers = {},
|
||||||
|
{ maxOutputLength }: { maxOutputLength?: number } = {},
|
||||||
|
{ timeout }: { timeout?: number } = {},
|
||||||
|
) {
|
||||||
|
super(...arguments);
|
||||||
|
|
||||||
|
this.maxOutputLength = maxOutputLength ?? this.maxOutputLength;
|
||||||
|
this.timeout = timeout ?? this.timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @ignore */
|
||||||
|
async _call(input: string) {
|
||||||
|
try {
|
||||||
|
const res = await this.fetchWithTimeout(
|
||||||
|
input,
|
||||||
|
{
|
||||||
|
headers: this.headers,
|
||||||
|
},
|
||||||
|
this.timeout,
|
||||||
|
);
|
||||||
|
let text = await res.text();
|
||||||
|
text = htmlToText(text);
|
||||||
|
text = text.slice(0, this.maxOutputLength);
|
||||||
|
console.log(text);
|
||||||
|
return text;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
return (error as Error).toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchWithTimeout(
|
||||||
|
resource: RequestInfo | URL,
|
||||||
|
options = {},
|
||||||
|
timeout: number = 30000,
|
||||||
|
) {
|
||||||
|
const controller = new AbortController();
|
||||||
|
const id = setTimeout(() => controller.abort(), timeout);
|
||||||
|
const response = await fetch(resource, {
|
||||||
|
...options,
|
||||||
|
signal: controller.signal,
|
||||||
|
});
|
||||||
|
clearTimeout(id);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
description = `A portal to the internet. Use this when you need to get specific content from a website.
|
||||||
|
Input should be a url string (i.e. "https://www.google.com"). The output will be the text response of the GET request.`;
|
||||||
|
}
|
@ -17,6 +17,7 @@ import { initializeAgentExecutorWithOptions } from "langchain/agents";
|
|||||||
import { SerpAPI } from "langchain/tools";
|
import { SerpAPI } from "langchain/tools";
|
||||||
import { Calculator } from "langchain/tools/calculator";
|
import { Calculator } from "langchain/tools/calculator";
|
||||||
import { DuckDuckGo } from "@/app/api/langchain-tools/duckduckgo";
|
import { DuckDuckGo } from "@/app/api/langchain-tools/duckduckgo";
|
||||||
|
import { HttpGetTool } from "@/app/api/langchain-tools/http_get";
|
||||||
|
|
||||||
const serverConfig = getServerSideConfig();
|
const serverConfig = getServerSideConfig();
|
||||||
|
|
||||||
@ -36,6 +37,7 @@ interface RequestBody {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ResponseBody {
|
class ResponseBody {
|
||||||
|
isSuccess: boolean = true;
|
||||||
message!: string;
|
message!: string;
|
||||||
isToolMessage: boolean = false;
|
isToolMessage: boolean = false;
|
||||||
toolName?: string;
|
toolName?: string;
|
||||||
@ -73,11 +75,17 @@ async function handle(req: NextRequest) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// async handleChainError(err, runId, parentRunId, tags) {
|
async handleChainError(err, runId, parentRunId, tags) {
|
||||||
// console.log("writer error");
|
console.log(err, "writer error");
|
||||||
// await writer.ready;
|
var response = new ResponseBody();
|
||||||
// await writer.abort(err);
|
response.isSuccess = false;
|
||||||
// },
|
response.message = err;
|
||||||
|
await writer.ready;
|
||||||
|
await writer.write(
|
||||||
|
encoder.encode(`data: ${JSON.stringify(response)}\n\n`),
|
||||||
|
);
|
||||||
|
await writer.close();
|
||||||
|
},
|
||||||
async handleChainEnd(outputs, runId, parentRunId, tags) {
|
async handleChainEnd(outputs, runId, parentRunId, tags) {
|
||||||
await writer.ready;
|
await writer.ready;
|
||||||
await writer.close();
|
await writer.close();
|
||||||
@ -87,9 +95,15 @@ async function handle(req: NextRequest) {
|
|||||||
// await writer.close();
|
// await writer.close();
|
||||||
},
|
},
|
||||||
async handleLLMError(e: Error) {
|
async handleLLMError(e: Error) {
|
||||||
console.log("writer error");
|
console.log(e, "writer error");
|
||||||
|
var response = new ResponseBody();
|
||||||
|
response.isSuccess = false;
|
||||||
|
response.message = e.message;
|
||||||
await writer.ready;
|
await writer.ready;
|
||||||
await writer.abort(e);
|
await writer.write(
|
||||||
|
encoder.encode(`data: ${JSON.stringify(response)}\n\n`),
|
||||||
|
);
|
||||||
|
await writer.close();
|
||||||
},
|
},
|
||||||
handleLLMStart(llm, _prompts: string[]) {
|
handleLLMStart(llm, _prompts: string[]) {
|
||||||
// console.log("handleLLMStart: I'm the second handler!!", { llm });
|
// console.log("handleLLMStart: I'm the second handler!!", { llm });
|
||||||
@ -115,6 +129,14 @@ async function handle(req: NextRequest) {
|
|||||||
);
|
);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
console.error("[handleAgentAction]", ex);
|
console.error("[handleAgentAction]", ex);
|
||||||
|
var response = new ResponseBody();
|
||||||
|
response.isSuccess = false;
|
||||||
|
response.message = (ex as Error).message;
|
||||||
|
await writer.ready;
|
||||||
|
await writer.write(
|
||||||
|
encoder.encode(`data: ${JSON.stringify(response)}\n\n`),
|
||||||
|
);
|
||||||
|
await writer.close();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleToolStart(tool, input) {
|
handleToolStart(tool, input) {
|
||||||
@ -134,8 +156,9 @@ async function handle(req: NextRequest) {
|
|||||||
|
|
||||||
const tools = [
|
const tools = [
|
||||||
searchTool,
|
searchTool,
|
||||||
new RequestsGetTool(),
|
new HttpGetTool(),
|
||||||
new RequestsPostTool(),
|
// new RequestsGetTool(),
|
||||||
|
// new RequestsPostTool(),
|
||||||
new Calculator(),
|
new Calculator(),
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -175,14 +198,12 @@ async function handle(req: NextRequest) {
|
|||||||
maxIterations: 3,
|
maxIterations: 3,
|
||||||
memory: memory,
|
memory: memory,
|
||||||
});
|
});
|
||||||
executor
|
executor.call(
|
||||||
.call(
|
|
||||||
{
|
{
|
||||||
input: reqBody.messages.slice(-1)[0].content,
|
input: reqBody.messages.slice(-1)[0].content,
|
||||||
},
|
},
|
||||||
[handler],
|
[handler],
|
||||||
)
|
);
|
||||||
.catch((e: Error) => console.error(e));
|
|
||||||
|
|
||||||
console.log("returning response");
|
console.log("returning response");
|
||||||
return new Response(transformStream.readable, {
|
return new Response(transformStream.readable, {
|
||||||
|
@ -2,7 +2,6 @@ import { getClientConfig } from "../config/client";
|
|||||||
import { ACCESS_CODE_PREFIX } from "../constant";
|
import { ACCESS_CODE_PREFIX } from "../constant";
|
||||||
import { ChatMessage, ModelType, useAccessStore } from "../store";
|
import { ChatMessage, ModelType, useAccessStore } from "../store";
|
||||||
import { ChatGPTApi } from "./platforms/openai";
|
import { ChatGPTApi } from "./platforms/openai";
|
||||||
import { DuckDuckGoSearch } from "./tools/ddg_search";
|
|
||||||
|
|
||||||
export const ROLES = ["system", "user", "assistant"] as const;
|
export const ROLES = ["system", "user", "assistant"] as const;
|
||||||
export type MessageRole = (typeof ROLES)[number];
|
export type MessageRole = (typeof ROLES)[number];
|
||||||
@ -80,11 +79,9 @@ export abstract class ToolApi {
|
|||||||
|
|
||||||
export class ClientApi {
|
export class ClientApi {
|
||||||
public llm: LLMApi;
|
public llm: LLMApi;
|
||||||
public searchTool: ToolApi;
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.llm = new ChatGPTApi();
|
this.llm = new ChatGPTApi();
|
||||||
this.searchTool = new DuckDuckGoSearch();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
config() {}
|
config() {}
|
||||||
|
@ -23,13 +23,6 @@ export interface OpenAIListModelResponse {
|
|||||||
}>;
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LangChainAgentResponse {
|
|
||||||
message: string;
|
|
||||||
isToolMessage: boolean;
|
|
||||||
toolName?: string;
|
|
||||||
toolInput?: object;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ChatGPTApi implements LLMApi {
|
export class ChatGPTApi implements LLMApi {
|
||||||
private disableListModels = true;
|
private disableListModels = true;
|
||||||
|
|
||||||
@ -165,6 +158,7 @@ export class ChatGPTApi implements LLMApi {
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("[Request] parse error", text, msg);
|
console.error("[Request] parse error", text, msg);
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onclose() {
|
onclose() {
|
||||||
@ -296,14 +290,15 @@ export class ChatGPTApi implements LLMApi {
|
|||||||
if (msg.data === "[DONE]" || finished) {
|
if (msg.data === "[DONE]" || finished) {
|
||||||
return finish();
|
return finish();
|
||||||
}
|
}
|
||||||
let response: LangChainAgentResponse = JSON.parse(msg.data);
|
let response = JSON.parse(msg.data);
|
||||||
|
if (!response.isSuccess) {
|
||||||
|
console.error("[Request]", response, msg);
|
||||||
|
throw Error(response.message);
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if (response && !response.isToolMessage) {
|
if (response && !response.isToolMessage) {
|
||||||
responseText += response.message;
|
responseText += response.message;
|
||||||
options.onUpdate?.(
|
options.onUpdate?.(responseText, response.message);
|
||||||
responseText,
|
|
||||||
JSON.stringify(response.toolInput),
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
options.onToolUpdate?.(response.toolName!, response.message);
|
options.onToolUpdate?.(response.toolName!, response.message);
|
||||||
}
|
}
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
import { ToolApi, getHeaders } from "../api";
|
|
||||||
|
|
||||||
export class DuckDuckGoSearch implements ToolApi {
|
|
||||||
name = "duckduckgo_search";
|
|
||||||
description =
|
|
||||||
"A wrapper around DuckDuckGo Search.Useful for when you need to answer questions about current events.Input should be a search query.";
|
|
||||||
|
|
||||||
async call(input: string): Promise<string> {
|
|
||||||
const res = await fetch(`/api/tools/ddg?query=${input}`, {
|
|
||||||
method: "GET",
|
|
||||||
headers: getHeaders(),
|
|
||||||
});
|
|
||||||
return await res.json();
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user