claude 3 api support (#642)

This commit is contained in:
josc146
2024-03-23 16:08:04 +08:00
parent 381cea38fe
commit e2ec8ac2e6
3 changed files with 41 additions and 30 deletions
+18 -3
View File
@@ -48,7 +48,14 @@ export const chatgptApiModelKeys = [
] ]
export const customApiModelKeys = ['customModel'] export const customApiModelKeys = ['customModel']
export const azureOpenAiApiModelKeys = ['azureOpenAi'] export const azureOpenAiApiModelKeys = ['azureOpenAi']
export const claudeApiModelKeys = ['claude2Api'] export const claudeApiModelKeys = [
'claude12Api',
'claude2Api',
'claude21Api',
'claude3HaikuApi',
'claude3SonnetApi',
'claude3OpusApi',
]
export const chatglmApiModelKeys = ['chatglmTurbo'] export const chatglmApiModelKeys = ['chatglmTurbo']
export const githubThirdPartyApiModelKeys = ['waylaidwandererApi'] export const githubThirdPartyApiModelKeys = ['waylaidwandererApi']
export const poeWebModelKeys = [ export const poeWebModelKeys = [
@@ -101,7 +108,15 @@ export const Models = {
}, },
claude2WebFree: { value: '', desc: 'Claude.ai (Web)' }, claude2WebFree: { value: '', desc: 'Claude.ai (Web)' },
claude2Api: { value: '', desc: 'Claude.ai (API, Claude 2)' }, claude12Api: { value: 'claude-instant-1.2', desc: 'Claude.ai (API, Claude Instant 1.2)' },
claude2Api: { value: 'claude-2.0', desc: 'Claude.ai (API, Claude 2)' },
claude21Api: { value: 'claude-2.1', desc: 'Claude.ai (API, Claude 2.1)' },
claude3HaikuApi: {
value: 'claude-3-haiku-20240307',
desc: 'Claude.ai (API, Claude 3 Haiku)',
},
claude3SonnetApi: { value: 'claude-3-sonnet-20240229', desc: 'Claude.ai (API, Claude 3 Sonnet)' },
claude3OpusApi: { value: 'claude-3-opus-20240229', desc: 'Claude.ai (API, Claude 3 Opus)' },
bingFree4: { value: '', desc: 'Bing (Web, GPT-4)' }, bingFree4: { value: '', desc: 'Bing (Web, GPT-4)' },
bingFreeSydney: { value: '', desc: 'Bing (Web, GPT-4, Sydney)' }, bingFreeSydney: { value: '', desc: 'Bing (Web, GPT-4, Sydney)' },
@@ -343,7 +358,7 @@ export function isUsingAzureOpenAi(configOrSession) {
return azureOpenAiApiModelKeys.includes(configOrSession.modelName) return azureOpenAiApiModelKeys.includes(configOrSession.modelName)
} }
export function isUsingClaude2Api(configOrSession) { export function isUsingClaudeApi(configOrSession) {
return claudeApiModelKeys.includes(configOrSession.modelName) return claudeApiModelKeys.includes(configOrSession.modelName)
} }
+3 -3
View File
@@ -5,7 +5,7 @@ import {
isUsingOpenAiApiKey, isUsingOpenAiApiKey,
isUsingAzureOpenAi, isUsingAzureOpenAi,
isUsingChatGLMApi, isUsingChatGLMApi,
isUsingClaude2Api, isUsingClaudeApi,
isUsingCustomModel, isUsingCustomModel,
isUsingCustomNameOnlyModel, isUsingCustomNameOnlyModel,
isUsingGithubThirdPartyApi, isUsingGithubThirdPartyApi,
@@ -145,7 +145,7 @@ export function GeneralPart({ config, updateConfig }) {
isUsingMultiModeModel(config) || isUsingMultiModeModel(config) ||
isUsingCustomModel(config) || isUsingCustomModel(config) ||
isUsingAzureOpenAi(config) || isUsingAzureOpenAi(config) ||
isUsingClaude2Api(config) || isUsingClaudeApi(config) ||
isUsingCustomNameOnlyModel(config) || isUsingCustomNameOnlyModel(config) ||
isUsingMoonshotApi(config) isUsingMoonshotApi(config)
? 'width: 50%;' ? 'width: 50%;'
@@ -264,7 +264,7 @@ export function GeneralPart({ config, updateConfig }) {
}} }}
/> />
)} )}
{isUsingClaude2Api(config) && ( {isUsingClaudeApi(config) && (
<input <input
type="password" type="password"
style="width: 50%;" style="width: 50%;"
+20 -24
View File
@@ -1,7 +1,8 @@
import { getUserConfig } from '../../config/index.mjs' import { getUserConfig, Models } from '../../config/index.mjs'
import { pushRecord, setAbortController } from './shared.mjs' import { pushRecord, setAbortController } from './shared.mjs'
import { fetchSSE } from '../../utils/fetch-sse.mjs' import { fetchSSE } from '../../utils/fetch-sse.mjs'
import { isEmpty } from 'lodash-es' import { isEmpty } from 'lodash-es'
import { getConversationPairs } from '../../utils/get-conversation-pairs.mjs'
/** /**
* @param {Runtime.Port} port * @param {Runtime.Port} port
@@ -11,28 +12,28 @@ import { isEmpty } from 'lodash-es'
export async function generateAnswersWithClaudeApi(port, question, session) { export async function generateAnswersWithClaudeApi(port, question, session) {
const { controller, messageListener, disconnectListener } = setAbortController(port) const { controller, messageListener, disconnectListener } = setAbortController(port)
const config = await getUserConfig() const config = await getUserConfig()
const modelName = session.modelName
let prompt = '' const prompt = getConversationPairs(
for (const record of session.conversationRecords.slice(-config.maxConversationContextLength)) { session.conversationRecords.slice(-config.maxConversationContextLength),
prompt += '\n\nHuman: ' + record.question + '\n\nAssistant: ' + record.answer false,
} )
prompt += `\n\nHuman: ${question}\n\nAssistant:` prompt.push({ role: 'user', content: question })
let answer = '' let answer = ''
await fetchSSE(`https://api.anthropic.com/v1/complete`, { await fetchSSE(`https://api.anthropic.com/v1/messages`, {
method: 'POST', method: 'POST',
signal: controller.signal, signal: controller.signal,
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
accept: 'application/json',
'anthropic-version': '2023-06-01', 'anthropic-version': '2023-06-01',
'x-api-key': config.claudeApiKey, 'x-api-key': config.claudeApiKey,
}, },
body: JSON.stringify({ body: JSON.stringify({
model: 'claude-2', model: Models[modelName].value,
prompt: prompt, messages: prompt,
stream: true, stream: true,
max_tokens_to_sample: config.maxResponseTokenLength, max_tokens: config.maxResponseTokenLength,
temperature: config.temperature, temperature: config.temperature,
}), }),
onMessage(message) { onMessage(message) {
@@ -45,22 +46,17 @@ export async function generateAnswersWithClaudeApi(port, question, session) {
console.debug('json error', error) console.debug('json error', error)
return return
} }
if (data?.type === 'message_stop') {
// 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) pushRecord(session, question, answer)
console.debug('conversation history', { content: session.conversationRecords }) console.debug('conversation history', { content: session.conversationRecords })
port.postMessage({ answer: null, done: true, session: session }) port.postMessage({ answer: null, done: true, session: session })
return
}
const delta = data?.delta?.text
if (delta) {
answer += delta
port.postMessage({ answer: answer, done: false, session: null })
} }
}, },
async onStart() {}, async onStart() {},