Skip to content

Commit

Permalink
Merge pull request #303 from AnyISalIn/main
Browse files Browse the repository at this point in the history
Add NovitaAI Provider
  • Loading branch information
VisargD authored May 1, 2024
2 parents 8dd2602 + 796c553 commit 29125f8
Show file tree
Hide file tree
Showing 7 changed files with 427 additions and 25 deletions.
51 changes: 26 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div align="center">
****<div align="center">

<p align="right">
<strong>English</strong> | <a href="./README.cn.md">中文</a>
Expand Down Expand Up @@ -145,22 +145,23 @@ For other providers, change the `provider` & `model` to their respective values.

Head over to [Portkey docs](https://portkey.ai/docs/product/ai-gateway-streamline-llm-integrations) for detailed [guides & cookbooks](https://portkey.ai/docs/welcome/integration-guides) on more [provider integrations](https://portkey.ai/docs/welcome/integration-guides).

|| Provider | Support | Stream |
|---|---|---|---|
| <img src="docs/images/openai.png" width=35 />| [OpenAI](https://portkey.ai/docs/welcome/integration-guides/openai) |||
| <img src="docs/images/azure.png" width=35>| [Azure OpenAI](https://portkey.ai/docs/welcome/integration-guides/azure-openai) |||
| <img src="docs/images/anyscale.png" width=35>| [Anyscale](https://portkey.ai/docs/welcome/integration-guides/anyscale-llama2-mistral-zephyr) |||
| <img src="https://upload.wikimedia.org/wikipedia/commons/2/2d/Google-favicon-2015.png" width=35>| [Google Gemini & Palm](https://portkey.ai/docs/welcome/integration-guides/gemini) |||
| <img src="docs/images/anthropic.png" width=35>| [Anthropic](https://portkey.ai/docs/welcome/integration-guides/anthropic) |||
| <img src="docs/images/cohere.png" width=35>| [Cohere](https://portkey.ai/docs/welcome/integration-guides/cohere) |||
| <img src="https://assets-global.website-files.com/64f6f2c0e3f4c5a91c1e823a/654693d569494912cfc0c0d4_favicon.svg" width=35>| [Together AI](https://portkey.ai/docs/welcome/integration-guides/together-ai) |||
| <img src="https://www.perplexity.ai/favicon.svg" width=35>| [Perplexity](https://portkey.ai/docs/welcome/integration-guides/perplexity-ai) |||
| <img src="https://docs.mistral.ai/img/favicon.ico" width=35>| [Mistral](https://portkey.ai/docs/welcome/integration-guides/mistral-ai) |||
| <img src="https://docs.nomic.ai/img/nomic-logo.png" width=35>| [Nomic](https://portkey.ai/docs/welcome/integration-guides/nomic) |||
| <img src="https://files.readme.io/d38a23e-small-studio-favicon.png" width=35>| [AI21](https://portkey.ai/docs/welcome/integration-guides) |||
| <img src="https://platform.stability.ai/small-logo-purple.svg" width=35>| [Stability AI](https://portkey.ai/docs/welcome/integration-guides/stability-ai) |||
| <img src="https://deepinfra.com/_next/static/media/logo.4a03fd3d.svg" width=35>| [DeepInfra](https://portkey.ai/docs/welcome/integration-guides) |||
| <img src="https://ollama.com/public/ollama.png" width=35>| [Ollama](https://portkey.ai/docs/welcome/integration-guides/ollama) |||
| | Provider | Support | Stream |
| -------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | ------- | ------ |
| <img src="docs/images/openai.png" width=35 /> | [OpenAI](https://portkey.ai/docs/welcome/integration-guides/openai) |||
| <img src="docs/images/azure.png" width=35> | [Azure OpenAI](https://portkey.ai/docs/welcome/integration-guides/azure-openai) |||
| <img src="docs/images/anyscale.png" width=35> | [Anyscale](https://portkey.ai/docs/welcome/integration-guides/anyscale-llama2-mistral-zephyr) |||
| <img src="https://upload.wikimedia.org/wikipedia/commons/2/2d/Google-favicon-2015.png" width=35> | [Google Gemini & Palm](https://portkey.ai/docs/welcome/integration-guides/gemini) |||
| <img src="docs/images/anthropic.png" width=35> | [Anthropic](https://portkey.ai/docs/welcome/integration-guides/anthropic) |||
| <img src="docs/images/cohere.png" width=35> | [Cohere](https://portkey.ai/docs/welcome/integration-guides/cohere) |||
| <img src="https://assets-global.website-files.com/64f6f2c0e3f4c5a91c1e823a/654693d569494912cfc0c0d4_favicon.svg" width=35> | [Together AI](https://portkey.ai/docs/welcome/integration-guides/together-ai) |||
| <img src="https://www.perplexity.ai/favicon.svg" width=35> | [Perplexity](https://portkey.ai/docs/welcome/integration-guides/perplexity-ai) |||
| <img src="https://docs.mistral.ai/img/favicon.ico" width=35> | [Mistral](https://portkey.ai/docs/welcome/integration-guides/mistral-ai) |||
| <img src="https://docs.nomic.ai/img/nomic-logo.png" width=35> | [Nomic](https://portkey.ai/docs/welcome/integration-guides/nomic) |||
| <img src="https://files.readme.io/d38a23e-small-studio-favicon.png" width=35> | [AI21](https://portkey.ai/docs/welcome/integration-guides) |||
| <img src="https://platform.stability.ai/small-logo-purple.svg" width=35> | [Stability AI](https://portkey.ai/docs/welcome/integration-guides/stability-ai) |||
| <img src="https://deepinfra.com/_next/static/media/logo.4a03fd3d.svg" width=35> | [DeepInfra](https://portkey.ai/docs/welcome/integration-guides) |||
| <img src="https://ollama.com/public/ollama.png" width=35> | [Ollama](https://portkey.ai/docs/welcome/integration-guides/ollama) |||
| <img src="https://novita.ai/favicon.ico" width=35> | Novita AI ||| `/chat/completions`, `/completions` |

> [View the complete list of 100+ supported models here](https://portkey.ai/docs/welcome/what-is-portkey#ai-providers-supported)
<br>
Expand Down Expand Up @@ -302,14 +303,14 @@ Here's a guide to [use the config object in your request](https://portkey.ai/doc

## Supported SDKs

| Language | Supported SDKs |
|---|---|
| Node.js / JS / TS | [Portkey SDK](https://www.npmjs.com/package/portkey-ai) <br> [OpenAI SDK](https://www.npmjs.com/package/openai) <br> [LangchainJS](https://www.npmjs.com/package/langchain) <br> [LlamaIndex.TS](https://www.npmjs.com/package/llamaindex) |
| Python | [Portkey SDK](https://pypi.org/project/portkey-ai/) <br> [OpenAI SDK](https://portkey.ai/docs/welcome/integration-guides/openai) <br> [Langchain](https://portkey.ai/docs/welcome/integration-guides/langchain-python) <br> [LlamaIndex](https://portkey.ai/docs/welcome/integration-guides/llama-index-python) |
| Go | [go-openai](https://github.com/sashabaranov/go-openai) |
| Java | [openai-java](https://github.com/TheoKanning/openai-java) |
| Rust | [async-openai](https://docs.rs/async-openai/latest/async_openai/) |
| Ruby | [ruby-openai](https://github.com/alexrudall/ruby-openai) |
| Language | Supported SDKs |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Node.js / JS / TS | [Portkey SDK](https://www.npmjs.com/package/portkey-ai) <br> [OpenAI SDK](https://www.npmjs.com/package/openai) <br> [LangchainJS](https://www.npmjs.com/package/langchain) <br> [LlamaIndex.TS](https://www.npmjs.com/package/llamaindex) |
| Python | [Portkey SDK](https://pypi.org/project/portkey-ai/) <br> [OpenAI SDK](https://portkey.ai/docs/welcome/integration-guides/openai) <br> [Langchain](https://portkey.ai/docs/welcome/integration-guides/langchain-python) <br> [LlamaIndex](https://portkey.ai/docs/welcome/integration-guides/llama-index-python) |
| Go | [go-openai](https://github.com/sashabaranov/go-openai) |
| Java | [openai-java](https://github.com/TheoKanning/openai-java) |
| Rust | [async-openai](https://docs.rs/async-openai/latest/async_openai/) |
| Ruby | [ruby-openai](https://github.com/alexrudall/ruby-openai) |
<br>


Expand Down
2 changes: 2 additions & 0 deletions src/globals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export const MOONSHOT: string = 'moonshot';
export const OPENROUTER: string = 'openrouter';
export const LINGYI: string = 'lingyi';
export const ZHIPU: string = 'zhipu';
export const NOVITA_AI: string = 'novita-ai';

export const VALID_PROVIDERS = [
ANTHROPIC,
Expand Down Expand Up @@ -76,6 +77,7 @@ export const VALID_PROVIDERS = [
OPENROUTER,
LINGYI,
ZHIPU,
NOVITA_AI,
];

export const CONTENT_TYPES = {
Expand Down
2 changes: 2 additions & 0 deletions src/providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import MoonshotConfig from './moonshot';
import OpenrouterConfig from './openrouter';
import LingYiConfig from './lingyi';
import ZhipuConfig from './zhipu';
import NovitaAIConfig from './novita-ai';

const Providers: { [key: string]: ProviderConfigs } = {
openai: OpenAIConfig,
Expand Down Expand Up @@ -53,6 +54,7 @@ const Providers: { [key: string]: ProviderConfigs } = {
openrouter: OpenrouterConfig,
lingyi: LingYiConfig,
zhipu: ZhipuConfig,
'novita-ai': NovitaAIConfig,
};

export default Providers;
20 changes: 20 additions & 0 deletions src/providers/novita-ai/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { ProviderAPIConfig } from '../types';

const NovitaAIApiConfig: ProviderAPIConfig = {
getBaseURL: () => 'https://api.novita.ai/v3/openai',
headers: ({ providerOptions }) => {
return { Authorization: `Bearer ${providerOptions.apiKey}` };
},
getEndpoint: ({ fn }) => {
switch (fn) {
case 'complete':
return '/v1/completions';
case 'chatComplete':
return '/v1/chat/completions';
default:
return '';
}
},
};

export default NovitaAIApiConfig;
220 changes: 220 additions & 0 deletions src/providers/novita-ai/chatComplete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
import { NOVITA_AI } from '../../globals';
import {
ChatCompletionResponse,
ErrorResponse,
ProviderConfig,
} from '../types';
import {
generateErrorResponse,
generateInvalidProviderResponseError,
} from '../utils';

// TODOS: this configuration does not enforce the maximum token limit for the input parameter. If you want to enforce this, you might need to add a custom validation function or a max property to the ParameterConfig interface, and then use it in the input configuration. However, this might be complex because the token count is not a simple length check, but depends on the specific tokenization method used by the model.

export const NovitaAIChatCompleteConfig: ProviderConfig = {
model: {
param: 'model',
required: true,
default: 'lzlv_70b',
},
messages: {
param: 'messages',
required: true,
default: '',
},
max_tokens: {
param: 'max_tokens',
required: true,
default: 128,
min: 1,
},
stop: {
param: 'stop',
},
temperature: {
param: 'temperature',
},
top_p: {
param: 'top_p',
},
n: {
param: 'n',
},
top_k: {
param: 'top_k',
},
presence_penalty: {
param: 'presence_penalty',
min: -2,
max: 2,
},
frequency_penalty: {
param: 'frequency_penalty',
min: -2,
max: 2,
},
stream: {
param: 'stream',
default: false,
},
logprobs: {
param: 'logprobs',
},
tools: {
param: 'tools',
},
tool_choice: {
param: 'tool_choice',
},
response_format: {
param: 'response_format',
},
};

export interface NovitaAIChatCompleteResponse extends ChatCompletionResponse {
usage: {
prompt_tokens: number;
completion_tokens: number;
total_tokens: number;
};
}

export interface NovitaAIErrorResponse {
model: string;
job_id: string;
request_id: string;
error: string;
message?: string;
type?: string;
}

export interface NovitaAIOpenAICompatibleErrorResponse extends ErrorResponse {}

export interface NovitaAIChatCompletionStreamChunk {
id: string;
request_id: string;
object: string;
choices: {
index: number;
delta: {
content: string;
};
}[];
}

export const NovitaAIErrorResponseTransform: (
response: NovitaAIErrorResponse | NovitaAIOpenAICompatibleErrorResponse
) => ErrorResponse | false = (response) => {
if ('error' in response && typeof response.error === 'string') {
return generateErrorResponse(
{ message: response.error, type: null, param: null, code: null },
NOVITA_AI
);
}

if ('error' in response && typeof response.error === 'object') {
return generateErrorResponse(
{
message: response.error?.message || '',
type: response.error?.type || null,
param: response.error?.param || null,
code: response.error?.code || null,
},
NOVITA_AI
);
}

if ('message' in response && response.message) {
return generateErrorResponse(
{
message: response.message,
type: response.type || null,
param: null,
code: null,
},
NOVITA_AI
);
}

return false;
};

export const NovitaAIChatCompleteResponseTransform: (
response:
| NovitaAIChatCompleteResponse
| NovitaAIErrorResponse
| NovitaAIOpenAICompatibleErrorResponse,
responseStatus: number
) => ChatCompletionResponse | ErrorResponse = (response, responseStatus) => {
if (responseStatus !== 200) {
const errorResponse = NovitaAIErrorResponseTransform(
response as NovitaAIErrorResponse
);
if (errorResponse) return errorResponse;
}

if ('choices' in response) {
return {
id: response.id,
object: response.object,
created: response.created,
model: response.model,
provider: NOVITA_AI,
choices: response.choices.map((choice) => {
return {
message: {
role: 'assistant',
content: choice.message.content,
tool_calls: choice.message.tool_calls
? choice.message.tool_calls.map((toolCall: any) => ({
id: toolCall.id,
type: toolCall.type,
function: toolCall.function,
}))
: null,
},
index: 0,
logprobs: null,
finish_reason: choice.finish_reason,
};
}),
usage: {
prompt_tokens: response.usage?.prompt_tokens,
completion_tokens: response.usage?.completion_tokens,
total_tokens: response.usage?.total_tokens,
},
};
}

return generateInvalidProviderResponseError(response, NOVITA_AI);
};

export const NovitaAIChatCompleteStreamChunkTransform: (
response: string
) => string = (responseChunk) => {
let chunk = responseChunk.trim();
chunk = chunk.replace(/^data: /, '');
chunk = chunk.trim();
if (chunk === '[DONE]') {
return `data: ${chunk}\n\n`;
}
const parsedChunk: NovitaAIChatCompletionStreamChunk = JSON.parse(chunk);
return (
`data: ${JSON.stringify({
id: parsedChunk.id,
object: parsedChunk.object,
created: Math.floor(Date.now() / 1000),
model: '',
provider: NOVITA_AI,
choices: [
{
delta: {
content: parsedChunk.choices[0]?.delta.content,
},
index: 0,
finish_reason: '',
},
],
})}` + '\n\n'
);
};
Loading

0 comments on commit 29125f8

Please sign in to comment.