2024-04-16 19:50:10 +02:00
const Anthropic = require ( "@anthropic-ai/sdk" ) ;
const { RetryError } = require ( "../error.js" ) ;
const Provider = require ( "./ai-provider.js" ) ;
/ * *
* The provider for the Anthropic API .
* By default , the model is set to 'claude-2' .
* /
class AnthropicProvider extends Provider {
model ;
constructor ( config = { } ) {
const {
options = {
apiKey : process . env . ANTHROPIC _API _KEY ,
maxRetries : 3 ,
} ,
model = "claude-2" ,
} = config ;
const client = new Anthropic ( options ) ;
super ( client ) ;
this . model = model ;
}
/ * *
* Create a completion based on the received messages .
*
* @ param messages A list of messages to send to the Anthropic API .
* @ param functions
* @ returns The completion .
* /
async complete ( messages , functions ) {
// clone messages to avoid mutating the original array
const promptMessages = [ ... messages ] ;
if ( functions ) {
const functionPrompt = this . getFunctionPrompt ( functions ) ;
// add function prompt after the first message
promptMessages . splice ( 1 , 0 , {
content : functionPrompt ,
role : "system" ,
} ) ;
}
const prompt = promptMessages
. map ( ( message ) => {
const { content , role } = message ;
switch ( role ) {
case "system" :
return content
? ` ${ Anthropic . HUMAN _PROMPT } <admin> ${ content } </admin> `
: "" ;
case "function" :
case "user" :
return ` ${ Anthropic . HUMAN _PROMPT } ${ content } ` ;
case "assistant" :
return ` ${ Anthropic . AI _PROMPT } ${ content } ` ;
default :
return content ;
}
} )
. filter ( Boolean )
. join ( "\n" )
. concat ( ` ${ Anthropic . AI _PROMPT } ` ) ;
try {
const response = await this . client . completions . create ( {
model : this . model ,
max _tokens _to _sample : 3000 ,
stream : false ,
prompt ,
} ) ;
const result = response . completion . trim ( ) ;
// TODO: get cost from response
const cost = 0 ;
// Handle function calls if the model returns a function call
if ( result . includes ( "function_name" ) && functions ) {
let functionCall ;
try {
functionCall = JSON . parse ( result ) ;
} catch ( error ) {
// call the complete function again in case it gets a json error
return await this . complete (
[
... messages ,
{
role : "function" ,
content : ` You gave me this function call: ${ result } but I couldn't parse it.
$ { error ? . message }
Please try again . ` ,
} ,
] ,
functions
) ;
}
return {
result : null ,
functionCall ,
cost ,
} ;
}
return {
result ,
cost ,
} ;
} catch ( error ) {
2024-04-17 01:42:06 +02:00
// If invalid Auth error we need to abort because no amount of waiting
// will make auth better.
if ( error instanceof Anthropic . AuthenticationError ) throw error ;
2024-04-16 19:50:10 +02:00
if (
error instanceof Anthropic . RateLimitError ||
error instanceof Anthropic . InternalServerError ||
2024-04-17 01:42:06 +02:00
error instanceof Anthropic . APIError // Also will catch AuthenticationError!!!
2024-04-16 19:50:10 +02:00
) {
throw new RetryError ( error . message ) ;
}
throw error ;
}
}
getFunctionPrompt ( functions = [ ] ) {
const functionPrompt = ` <functions>You have been trained to directly call a Javascript function passing a JSON Schema parameter as a response to this chat. This function will return a string that you can use to keep chatting.
Here is a list of functions available to you :
$ { JSON . stringify ( functions , null , 2 ) }
When calling any of those function in order to complete your task , respond only this JSON format . Do not include any other information or any other stuff .
Function call format :
{
function _name : "givenfunctionname" ,
parameters : { }
}
< / f u n c t i o n s > ` ;
return functionPrompt ;
}
}
module . exports = AnthropicProvider ;