From 3ea6406f3718fb6eb0c4c448700bfdf0e47907d2 Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 19 Nov 2024 07:06:44 +0000 Subject: [PATCH 1/2] feat: add new llm provider Novita AI --- .../docs/integrations/chat/novita.mdx | 51 ++++++ .../src/models/chat/integration_novita.ts | 25 +++ .../models/chat/integration_novita_chain.ts | 29 ++++ libs/langchain-community/langchain.config.js | 1 + libs/langchain-community/package.json | 2 +- .../src/chat_models/novita.ts | 147 ++++++++++++++++++ .../chat_models/tests/chatnovita.int.test.ts | 91 +++++++++++ 7 files changed, 345 insertions(+), 1 deletion(-) create mode 100644 docs/core_docs/docs/integrations/chat/novita.mdx create mode 100644 examples/src/models/chat/integration_novita.ts create mode 100644 examples/src/models/chat/integration_novita_chain.ts create mode 100644 libs/langchain-community/src/chat_models/novita.ts create mode 100644 libs/langchain-community/src/chat_models/tests/chatnovita.int.test.ts diff --git a/docs/core_docs/docs/integrations/chat/novita.mdx b/docs/core_docs/docs/integrations/chat/novita.mdx new file mode 100644 index 000000000000..7b822bd56461 --- /dev/null +++ b/docs/core_docs/docs/integrations/chat/novita.mdx @@ -0,0 +1,51 @@ +--- +sidebar_label: Novita +--- + +import CodeBlock from "@theme/CodeBlock"; + +# ChatNovita + +[Novita AI](https://novita.ai/model-api/product/llm-api?utm_source=github_langchain&utm_medium=github_readme&utm_campaign=link) is the most cost-effective, stable, and scalable inference platform — delivering production-ready performance at a lower cost. + +This guide will help you get started with `ChatNovitaAI` [chat models](/docs/concepts/chat_models). For detailed documentation of all `ChatNovitaAI` features and configurations head to the [API reference](https://novita.ai/docs/model-api/reference/llm/llm.html?utm_source=github_langchain&utm_medium=github_readme&utm_campaign=link). + +## Setup + +To access Novita AI models you'll need to create a Novita account and get an API key. + +### Credentials + +Head to [this page](https://novita.ai/settings#key-management?utm_source=github_langchain&utm_medium=github_readme&utm_campaign=link) to sign up to Novita AI and generate an API key. Once you've done this set the NOVITA_API_KEY environment variable: + +```bash +export NOVITA_API_KEY="your-api-key" +``` + +### Installation + +The LangChain Novita integration lives in the `@langchain-community` package: + +import IntegrationInstallTooltip from "@mdx_components/integration_install_tooltip.mdx"; + + + +```bash npm2yarn +npm install @langchain/community @langchain/core +``` + +## Basic usage + +import Novita from "@examples/models/chat/integration_novita.ts"; + +{Novita} + +## Chain model calls + +import NovitaChain from "@examples/models/chat/integration_novita_chain.ts"; + +{NovitaChain} + +## API reference + +For detailed documentation of Novita AI LLM APIs, head to [Novita AI API reference](https://novita.ai/docs/model-api/reference/llm/llm.html?utm_source=github_langchain&utm_medium=github_readme&utm_campaign=link) \ No newline at end of file diff --git a/examples/src/models/chat/integration_novita.ts b/examples/src/models/chat/integration_novita.ts new file mode 100644 index 000000000000..12ce2c3844ca --- /dev/null +++ b/examples/src/models/chat/integration_novita.ts @@ -0,0 +1,25 @@ +import { ChatNovita } from "@langchain/community/chat_models/novita"; +import { HumanMessage } from "@langchain/core/messages"; + +// Use gryphe/mythomax-l2-13b +const chat = new ChatNovita({ + apiKey: "YOUR-API-KEY", // In Node.js defaults to process.env.NOVITA_API_KEY + model: "gryphe/mythomax-l2-13b", // Check available models at https://novita.ai/llm-api + temperature: 0.3, +}); + +const messages = [new HumanMessage("Hello")]; + +const res = await chat.invoke(messages); +/* +AIMessage { + content: "Hello! How can I help you today? Is there something you would like to talk about or ask about? I'm here to assist you with any questions you may have.", +} +*/ + +const res2 = await chat.invoke(messages); +/* +AIMessage { + text: "Hello! How can I help you today? Is there something you would like to talk about or ask about? I'm here to assist you with any questions you may have.", +} +*/ diff --git a/examples/src/models/chat/integration_novita_chain.ts b/examples/src/models/chat/integration_novita_chain.ts new file mode 100644 index 000000000000..482e21f70509 --- /dev/null +++ b/examples/src/models/chat/integration_novita_chain.ts @@ -0,0 +1,29 @@ +import { ChatNovita } from "@langchain/community/chat_models/novita"; +import { LLMChain } from "langchain/chains"; +import { PromptTemplate } from "@langchain/core/prompts"; + + +const chat = new ChatNovita({ + apiKey: "YOUR-API-KEY", // In Node.js defaults to process.env.NOVITA_API_KEY + model: "gryphe/mythomax-l2-13b", // Check available models at https://novita.ai/llm-api + temperature: 0.3, +}); + +const prompt = PromptTemplate.fromTemplate( + "What is a good name for a company that makes {product}?" +); +const chain = new LLMChain({ llm: chat, prompt }); + +const response = await chain.invoke({ product: "colorful socks" }); + +console.log({ response }); + +/* + { + text: `I'm not sure what you mean by "colorful socks" but here are some ideas:\n` + + '\n' + + '- Sock-it to me!\n' + + '- Socks Away\n' + + '- Fancy Footwear' + } +*/ diff --git a/libs/langchain-community/langchain.config.js b/libs/langchain-community/langchain.config.js index 63b495f92f2c..2e63d51c566a 100644 --- a/libs/langchain-community/langchain.config.js +++ b/libs/langchain-community/langchain.config.js @@ -113,6 +113,7 @@ export const config = { "llms/writer": "llms/writer", "llms/yandex": "llms/yandex", "llms/layerup_security": "llms/layerup_security", + "llms/novita": "llms/novita", // vectorstores "vectorstores/analyticdb": "vectorstores/analyticdb", "vectorstores/astradb": "vectorstores/astradb", diff --git a/libs/langchain-community/package.json b/libs/langchain-community/package.json index a1f60050f981..f6d0352033eb 100644 --- a/libs/langchain-community/package.json +++ b/libs/langchain-community/package.json @@ -35,7 +35,7 @@ "author": "LangChain", "license": "MIT", "dependencies": { - "@langchain/openai": ">=0.2.0 <0.4.0", + "@langchain/openai": "workspace:*", "binary-extensions": "^2.2.0", "expr-eval": "^2.0.2", "flat": "^5.0.2", diff --git a/libs/langchain-community/src/chat_models/novita.ts b/libs/langchain-community/src/chat_models/novita.ts new file mode 100644 index 000000000000..59726db84238 --- /dev/null +++ b/libs/langchain-community/src/chat_models/novita.ts @@ -0,0 +1,147 @@ +import type { + BaseChatModelParams, + LangSmithParams, +} from "@langchain/core/language_models/chat_models"; +import { + type OpenAIClient, + type ChatOpenAICallOptions, + type OpenAIChatInput, + type OpenAICoreRequestOptions, + ChatOpenAI, +} from "@langchain/openai"; +import { getEnvironmentVariable } from "@langchain/core/utils/env"; + +type NovitaUnsupportedArgs = + | "frequencyPenalty" + | "presencePenalty" + | "logitBias" + | "functions"; + +type NovitaUnsupportedCallOptions = "functions" | "function_call"; + +export interface ChatNovitaCallOptions + extends Omit { + response_format: { + type: "json_object"; + schema: Record; + }; +} + +export interface ChatNovitaInput + extends Omit, + BaseChatModelParams { + /** + * Novita API key + * @default process.env.NOVITA_API_KEY + */ + novitaApiKey?: string; + /** + * API key alias + * @default process.env.NOVITA_API_KEY + */ + apiKey?: string; +} + +/** + * Novita chat model implementation + */ +export class ChatNovitaAI extends ChatOpenAI { + static lc_name() { + return "ChatNovita"; + } + + _llmType() { + return "novita"; + } + + get lc_secrets(): { [key: string]: string } | undefined { + return { + novitaApiKey: "NOVITA_API_KEY", + apiKey: "NOVITA_API_KEY", + }; + } + + lc_serializable = true; + + constructor( + fields?: Partial< + Omit + > & + BaseChatModelParams & { + novitaApiKey?: string; + apiKey?: string; + } + ) { + const novitaApiKey = + fields?.apiKey || + fields?.novitaApiKey || + getEnvironmentVariable("NOVITA_API_KEY"); + + if (!novitaApiKey) { + throw new Error( + `Novita API key not found. Please set the NOVITA_API_KEY environment variable or provide the key into "novitaApiKey"` + ); + } + + super({ + ...fields, + model: fields?.model || "gryphe/mythomax-l2-13b", + apiKey: novitaApiKey, + configuration: { + baseURL: "https://api.novita.ai/v3/openai/", + }, + }); + } + + getLsParams(options: this["ParsedCallOptions"]): LangSmithParams { + const params = super.getLsParams(options); + params.ls_provider = "novita"; + return params; + } + + toJSON() { + const result = super.toJSON(); + + if ( + "kwargs" in result && + typeof result.kwargs === "object" && + result.kwargs != null + ) { + delete result.kwargs.openai_api_key; + delete result.kwargs.configuration; + } + + return result; + } + + async completionWithRetry( + request: OpenAIClient.Chat.ChatCompletionCreateParamsStreaming, + options?: OpenAICoreRequestOptions + ): Promise>; + + async completionWithRetry( + request: OpenAIClient.Chat.ChatCompletionCreateParamsNonStreaming, + options?: OpenAICoreRequestOptions + ): Promise; + + async completionWithRetry( + request: + | OpenAIClient.Chat.ChatCompletionCreateParamsStreaming + | OpenAIClient.Chat.ChatCompletionCreateParamsNonStreaming, + options?: OpenAICoreRequestOptions + ): Promise< + | AsyncIterable + | OpenAIClient.Chat.Completions.ChatCompletion + > { + delete request.frequency_penalty; + delete request.presence_penalty; + delete request.logit_bias; + delete request.functions; + + if (request.stream === true) { + return super.completionWithRetry(request, options); + } + + return super.completionWithRetry(request, options); + } +} diff --git a/libs/langchain-community/src/chat_models/tests/chatnovita.int.test.ts b/libs/langchain-community/src/chat_models/tests/chatnovita.int.test.ts new file mode 100644 index 000000000000..91a134d45c57 --- /dev/null +++ b/libs/langchain-community/src/chat_models/tests/chatnovita.int.test.ts @@ -0,0 +1,91 @@ +import { describe, test } from "@jest/globals"; +import { ChatMessage, HumanMessage } from "@langchain/core/messages"; +import { + PromptTemplate, + ChatPromptTemplate, + AIMessagePromptTemplate, + HumanMessagePromptTemplate, + SystemMessagePromptTemplate, +} from "@langchain/core/prompts"; +import { ChatNovitaAI } from "../novita.js"; + +describe("ChatNovitaAI", () => { + test("invoke", async () => { + const chat = new ChatNovitaAI(); + const message = new HumanMessage("Hello!"); + const res = await chat.invoke([message]); + expect(res.content.length).toBeGreaterThan(10); + }); + + test("generate", async () => { + const chat = new ChatNovitaAI(); + const message = new HumanMessage("Hello!"); + const res = await chat.generate([[message]]); + expect(res.generations[0][0].text.length).toBeGreaterThan(10); + }); + + test("custom messages", async () => { + const chat = new ChatNovitaAI(); + const res = await chat.invoke([new ChatMessage("Hello!", "user")]); + expect(res.content.length).toBeGreaterThan(2); + }); + + test("chaining", async () => { + const chat = new ChatNovitaAI(); + const prompt = ChatPromptTemplate.fromMessages([ + [ + "system", + "You are a helpful assistant that translates {input_language} to {output_language}.", + ], + ["human", "{input}"], + ]); + + const chain = prompt.pipe(chat); + const response = await chain.invoke({ + input_language: "English", + output_language: "German", + input: "I love programming.", + }); + + expect(response.content.length).toBeGreaterThan(10); + }); + + test("prompt templates", async () => { + const chat = new ChatNovitaAI(); + + // PaLM doesn't support translation yet + const systemPrompt = PromptTemplate.fromTemplate( + "You are a helpful assistant who must always respond like a {job}." + ); + + const chatPrompt = ChatPromptTemplate.fromMessages([ + new SystemMessagePromptTemplate(systemPrompt), + HumanMessagePromptTemplate.fromTemplate("{text}"), + ]); + + const responseA = await chat.generatePrompt([ + await chatPrompt.formatPromptValue({ + job: "pirate", + text: "What would be a good company name a company that makes colorful socks?", + }), + ]); + expect(responseA.generations[0][0].text.length).toBeGreaterThan(10); + }); + + test("longer chain of messages", async () => { + const chat = new ChatNovitaAI(); + + const chatPrompt = ChatPromptTemplate.fromMessages([ + HumanMessagePromptTemplate.fromTemplate(`Hi, my name is Joe!`), + AIMessagePromptTemplate.fromTemplate(`Nice to meet you, Joe!`), + HumanMessagePromptTemplate.fromTemplate("{text}"), + ]); + + const responseA = await chat.generatePrompt([ + await chatPrompt.formatPromptValue({ + text: "What did I just say my name was?", + }), + ]); + expect(responseA.generations[0][0].text.length).toBeGreaterThan(10); + }); +}); From 30c9dee7979b51d57de7cbf66e4d6b9b0f767348 Mon Sep 17 00:00:00 2001 From: Jason Date: Tue, 19 Nov 2024 19:28:15 +0800 Subject: [PATCH 2/2] fix: fix novita docs --- docs/core_docs/.gitignore | 6 + .../docs/integrations/chat/novita.ipynb | 207 ++++++++++++++++++ 2 files changed, 213 insertions(+) create mode 100644 docs/core_docs/docs/integrations/chat/novita.ipynb diff --git a/docs/core_docs/.gitignore b/docs/core_docs/.gitignore index 099af2f2d642..6e13afcf259a 100644 --- a/docs/core_docs/.gitignore +++ b/docs/core_docs/.gitignore @@ -268,6 +268,8 @@ docs/integrations/toolkits/openapi.md docs/integrations/toolkits/openapi.mdx docs/integrations/text_embedding/togetherai.md docs/integrations/text_embedding/togetherai.mdx +docs/integrations/text_embedding/pinecone.md +docs/integrations/text_embedding/pinecone.mdx docs/integrations/text_embedding/openai.md docs/integrations/text_embedding/openai.mdx docs/integrations/text_embedding/ollama.md @@ -328,12 +330,16 @@ docs/integrations/llms/azure.md docs/integrations/llms/azure.mdx docs/integrations/llms/arcjet.md docs/integrations/llms/arcjet.mdx +docs/integrations/chat/xai.md +docs/integrations/chat/xai.mdx docs/integrations/chat/togetherai.md docs/integrations/chat/togetherai.mdx docs/integrations/chat/openai.md docs/integrations/chat/openai.mdx docs/integrations/chat/ollama.md docs/integrations/chat/ollama.mdx +docs/integrations/chat/novita.md +docs/integrations/chat/novita.mdx docs/integrations/chat/mistral.md docs/integrations/chat/mistral.mdx docs/integrations/chat/ibm.md diff --git a/docs/core_docs/docs/integrations/chat/novita.ipynb b/docs/core_docs/docs/integrations/chat/novita.ipynb new file mode 100644 index 000000000000..00035517572a --- /dev/null +++ b/docs/core_docs/docs/integrations/chat/novita.ipynb @@ -0,0 +1,207 @@ +{ + "cells": [ + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "---\n", + "sidebar_label: Novita\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# ChatNovita\n", + "\n", + "[Novita AI](https://novita.ai/model-api/product/llm-api?utm_source=github_langchain&utm_medium=github_readme&utm_campaign=link): The most cost-effective, stable, and scalable inference platform — delivering production-ready performance at a lower cost.\n", + "\n", + "This guide will help you getting started with `ChatNovitaAI` [chat models](/docs/concepts/chat_models). For detailed documentation of all `ChatNovitaAI` features and configurations head to the [API reference](https://novita.ai/docs/model-api/reference/llm/llm.html?utm_source=github_langchain&utm_medium=github_readme&utm_campaign=link)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Overview\n", + "\n", + "### Model features\n", + "| [Tool calling](../../how_to/tool_calling.ipynb) | [Structured output](../../how_to/structured_output.ipynb) | JSON mode | [Image input](../../how_to/multimodal_inputs.ipynb) | Audio input | Video input | [Token-level streaming](../../how_to/chat_streaming.ipynb) | Native async | [Token usage](../../how_to/chat_token_usage_tracking.ipynb) | [Logprobs](../../how_to/logprobs.ipynb) |\n", + "| :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |\n", + "| ❌ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Setup\n", + "\n", + "To access Novita AI models you'll need to create a Novita account and get an API key.\n", + "\n", + "### Credentials\n", + "\n", + "Head to [this page](https://novita.ai/settings#key-management?utm_source=github_langchain&utm_medium=github_readme&utm_campaign=link) to sign up to Novita AI and generate an API key. Once you've done this set the NOVITA_API_KEY environment variable:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "shellscript" + } + }, + "outputs": [], + "source": [ + "export NOVITA_API_KEY=\"your-api-key\"" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Installation\n", + "\n", + "The LangChain Novita integration lives in the `@langchain-community` package:\n", + "\n", + "```{=mdx}\n", + "import IntegrationInstallTooltip from \"@mdx_components/integration_install_tooltip.mdx\";\n", + "import Npm2Yarn from \"@theme/Npm2Yarn\";\n", + "\n", + "\n", + "\n", + "\n", + " @langchain/community @langchain/core\n", + "\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Instantiation\n", + "\n", + "Now we can instantiate our model object and generate chat completions:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "javascript" + } + }, + "outputs": [], + "source": [ + "import { ChatNovitaAI } from \"../novita.js\";\n", + "\n", + "const llm = new ChatNovitaAI({\n", + " model: \"gryphe/mythomax-l2-13b\",\n", + " temperature: 0,\n", + " // other params...\n", + "})" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Invocation" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "javascript" + } + }, + "outputs": [], + "source": [ + "const aiMsg = await llm.invoke([\n", + " [\n", + " \"system\",\n", + " \"You are a helpful assistant that translates English to French. Translate the user sentence.\",\n", + " ],\n", + " [\"human\", \"I love programming.\"],\n", + "])\n", + "aiMsg" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "javascript" + } + }, + "outputs": [], + "source": [ + "console.log(aiMsg.content)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Chaining\n", + "\n", + "We can [chain](../../how_to/sequence.ipynb) our model with a prompt template like so:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "vscode": { + "languageId": "javascript" + } + }, + "outputs": [], + "source": [ + "import { ChatPromptTemplate } from \"@langchain/core/prompts\"\n", + "\n", + "const prompt = ChatPromptTemplate.fromMessages(\n", + " [\n", + " [\n", + " \"system\",\n", + " \"You are a helpful assistant that translates {input_language} to {output_language}.\",\n", + " ],\n", + " [\"human\", \"{input}\"],\n", + " ]\n", + ")\n", + "\n", + "const chain = prompt.pipe(llm);\n", + "await chain.invoke(\n", + " {\n", + " input_language: \"English\",\n", + " output_language: \"German\",\n", + " input: \"I love programming.\",\n", + " }\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## API reference\n", + "\n", + "For detailed documentation of Novita AI LLM APIs, head to [Novita AI API reference](https://novita.ai/docs/model-api/reference/llm/llm.html?utm_source=github_langchain&utm_medium=github_readme&utm_campaign=link)\n" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}