mirror of
https://github.com/wassname/chatGPTBox.git
synced 2026-07-07 05:33:52 +08:00
feat: Add claude-v2 API (#516)
This commit is contained in:
@@ -11,10 +11,12 @@ import {
|
||||
} from '../services/apis/openai-api'
|
||||
import { generateAnswersWithCustomApi } from '../services/apis/custom-api.mjs'
|
||||
import { generateAnswersWithAzureOpenaiApi } from '../services/apis/azure-openai-api.mjs'
|
||||
import { generateAnswersWithClaudeApi } from '../services/apis/claude-api.mjs'
|
||||
import { generateAnswersWithWaylaidwandererApi } from '../services/apis/waylaidwanderer-api.mjs'
|
||||
import { generateAnswersWithPoeWebApi } from '../services/apis/poe-web.mjs'
|
||||
import {
|
||||
azureOpenAiApiModelKeys,
|
||||
claudeApiModelKeys,
|
||||
bardWebModelKeys,
|
||||
bingWebModelKeys,
|
||||
chatgptApiModelKeys,
|
||||
@@ -111,6 +113,8 @@ async function executeApi(session, port, config) {
|
||||
await generateAnswersWithCustomApi(port, session.question, session, '', config.customModelName)
|
||||
} else if (azureOpenAiApiModelKeys.includes(session.modelName)) {
|
||||
await generateAnswersWithAzureOpenaiApi(port, session.question, session)
|
||||
} else if (claudeApiModelKeys.includes(session.modelName)) {
|
||||
await generateAnswersWithClaudeApi(port, session.question, session)
|
||||
} else if (githubThirdPartyApiModelKeys.includes(session.modelName)) {
|
||||
await generateAnswersWithWaylaidwandererApi(port, session.question, session)
|
||||
} else if (poeWebModelKeys.includes(session.modelName)) {
|
||||
|
||||
@@ -44,6 +44,7 @@ export const chatgptApiModelKeys = [
|
||||
]
|
||||
export const customApiModelKeys = ['customModel']
|
||||
export const azureOpenAiApiModelKeys = ['azureOpenAi']
|
||||
export const claudeApiModelKeys = ['claude2Api']
|
||||
export const githubThirdPartyApiModelKeys = ['waylaidwandererApi']
|
||||
export const poeWebModelKeys = [
|
||||
'poeAiWebSage', //poe.com/Assistant
|
||||
@@ -83,6 +84,7 @@ export const Models = {
|
||||
desc: 'ChatGPT (GPT-3.5-turbo-16k 0613)',
|
||||
},
|
||||
claude2WebFree: { value: 'claude-2', desc: 'Claude.ai (Web, Claude 2)' },
|
||||
claude2Api: { value: '', desc: 'Claude.ai (API, Claude 2)' },
|
||||
bingFree4: { value: '', desc: 'Bing (Web, GPT-4)' },
|
||||
bingFreeSydney: { value: '', desc: 'Bing (Web, GPT-4, Sydney)' },
|
||||
bardWebFree: { value: '', desc: 'Bard (Web)' },
|
||||
@@ -149,6 +151,7 @@ export const defaultConfig = {
|
||||
|
||||
poeCustomBotName: '',
|
||||
|
||||
claudeApiKey: '',
|
||||
/** @type {keyof ModelMode}*/
|
||||
modelMode: 'balanced',
|
||||
|
||||
@@ -175,6 +178,7 @@ export const defaultConfig = {
|
||||
|
||||
alwaysCreateNewConversationWindow: false,
|
||||
activeApiModes: [
|
||||
// 'claude2Api',
|
||||
'chatgptFree35',
|
||||
'chatgptFree35Mobile',
|
||||
// 'chatgptPlus4',
|
||||
@@ -288,6 +292,9 @@ export function isUsingAzureOpenAi(configOrSession) {
|
||||
return azureOpenAiApiModelKeys.includes(configOrSession.modelName)
|
||||
}
|
||||
|
||||
export function isUsingClaude2Api(configOrSession) {
|
||||
return claudeApiModelKeys.includes(configOrSession.modelName)
|
||||
}
|
||||
export function isUsingGithubThirdPartyApi(configOrSession) {
|
||||
return githubThirdPartyApiModelKeys.includes(configOrSession.modelName)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { openUrl } from '../../utils/index.mjs'
|
||||
import {
|
||||
isUsingApiKey,
|
||||
isUsingAzureOpenAi,
|
||||
isUsingClaude2Api,
|
||||
isUsingCustomModel,
|
||||
isUsingCustomNameOnlyModel,
|
||||
isUsingGithubThirdPartyApi,
|
||||
@@ -142,6 +143,7 @@ export function GeneralPart({ config, updateConfig }) {
|
||||
isUsingMultiModeModel(config) ||
|
||||
isUsingCustomModel(config) ||
|
||||
isUsingAzureOpenAi(config) ||
|
||||
isUsingClaude2Api(config) ||
|
||||
isUsingCustomNameOnlyModel(config)
|
||||
? 'width: 50%;'
|
||||
: undefined
|
||||
@@ -259,6 +261,18 @@ export function GeneralPart({ config, updateConfig }) {
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{isUsingClaude2Api(config) && (
|
||||
<input
|
||||
type="password"
|
||||
style="width: 50%;"
|
||||
value={config.claudeApiKey}
|
||||
placeholder={t('Claude API Key')}
|
||||
onChange={(e) => {
|
||||
const apiKey = e.target.value
|
||||
updateConfig({ claudeApiKey: apiKey })
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
{isUsingCustomModel(config) && (
|
||||
<input
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
import { getUserConfig } from '../../config/index.mjs'
|
||||
import { getChatSystemPromptBase, pushRecord, setAbortController } from './shared.mjs'
|
||||
import { getConversationPairs } from '../../utils/get-conversation-pairs.mjs'
|
||||
import { fetchSSE } from '../../utils/fetch-sse.mjs'
|
||||
import { isEmpty } from 'lodash-es'
|
||||
|
||||
/**
|
||||
* @param {Runtime.Port} port
|
||||
* @param {string} question
|
||||
* @param {Session} session
|
||||
*/
|
||||
export async function generateAnswersWithClaudeApi(port, question, session) {
|
||||
const { controller, messageListener, disconnectListener } = setAbortController(port)
|
||||
const config = await getUserConfig()
|
||||
|
||||
const prompt = getConversationPairs(
|
||||
session.conversationRecords.slice(-config.maxConversationContextLength),
|
||||
false,
|
||||
)
|
||||
prompt.unshift({ role: 'Assistant', content: await getChatSystemPromptBase() })
|
||||
prompt.push({ role: 'Human', content: question })
|
||||
|
||||
let answer = ''
|
||||
await fetchSSE(
|
||||
`https://api.anthropic.com/v1/complete`,
|
||||
{
|
||||
method: 'POST',
|
||||
signal: controller.signal,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'accept': 'application/json',
|
||||
'anthropic-version': '2023-06-01',
|
||||
'x-api-key': config.claudeApiKey,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: "claude-2",
|
||||
prompt: "\n\nHuman: " + question + "\n\nAssistant:",
|
||||
stream: true,
|
||||
max_tokens_to_sample: config.maxResponseTokenLength,
|
||||
temperature: config.temperature,
|
||||
}),
|
||||
onMessage(message) {
|
||||
console.debug('sse message', message);
|
||||
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(message);
|
||||
} catch (error) {
|
||||
console.debug('json error', error);
|
||||
return;
|
||||
}
|
||||
|
||||
// The Claude v2 API may send metadata fields, handle them here
|
||||
if (data.conversationId) session.conversationId = data.conversationId;
|
||||
if (data.parentMessageId) session.parentMessageId = data.parentMessageId;
|
||||
|
||||
// In Claude's case, the "completion" key holds the text
|
||||
if (data.completion) {
|
||||
answer += data.completion;
|
||||
port.postMessage({ answer: answer, done: false, session: null });
|
||||
}
|
||||
|
||||
// Check if the message indicates that Claude is done
|
||||
if (data.stop_reason === 'stop_sequence') {
|
||||
pushRecord(session, question, answer);
|
||||
console.debug('conversation history', { content: session.conversationRecords });
|
||||
port.postMessage({ answer: null, done: true, session: session });
|
||||
}
|
||||
},
|
||||
async onStart() {},
|
||||
async onEnd() {
|
||||
port.postMessage({ done: true })
|
||||
port.onMessage.removeListener(messageListener)
|
||||
port.onDisconnect.removeListener(disconnectListener)
|
||||
},
|
||||
async onError(resp) {
|
||||
port.onMessage.removeListener(messageListener)
|
||||
port.onDisconnect.removeListener(disconnectListener)
|
||||
if (resp instanceof Error) throw resp
|
||||
const error = await resp.json().catch(() => ({}))
|
||||
throw new Error(
|
||||
!isEmpty(error) ? JSON.stringify(error) : `${resp.status} ${resp.statusText}`,
|
||||
)
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user