From ce5c9bcd72637749b332f5f37b9c65799264ab11 Mon Sep 17 00:00:00 2001 From: u1and0 Date: Sun, 22 Dec 2024 07:01:24 +0900 Subject: [PATCH 01/32] [add] groq --- lib/llm.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/llm.ts b/lib/llm.ts index e27ca52..a1b2621 100644 --- a/lib/llm.ts +++ b/lib/llm.ts @@ -2,6 +2,7 @@ import { ChatOpenAI } from "npm:@langchain/openai"; import { ChatAnthropic } from "npm:@langchain/anthropic"; import { ChatOllama } from "npm:@langchain/community/chat_models/ollama"; import { ChatGoogleGenerativeAI } from "npm:@langchain/google-genai"; +import { ChatGroq } from "npm:@langchain/groq"; import Replicate from "npm:replicate"; import ServerSentEvent from "npm:replicate"; import { @@ -26,6 +27,7 @@ export class LLM { | ChatAnthropic | ChatOllama | ChatGoogleGenerativeAI + | ChatGroq | Replicate | undefined; @@ -75,6 +77,12 @@ export class LLM { temperature: params.temperature, maxOutputTokens: params.maxTokens, }); + } else if (params.model.startsWith("llama")) { + return new ChatGroq({ + model: params.model, + temperature: params.temperature, + maxOutputTokens: params.maxTokens, + }); } else if ( // params.modelの文字列にollamaModelsのうちの一部が含まれていたらtrue replicateModelPatterns.some((p: RegExp) => p.test(params.model)) && // replicateモデルのパターンに一致 From 7dd72190a82acd3e06d7adfd12e50ab2069c999d Mon Sep 17 00:00:00 2001 From: u1and0 Date: Mon, 23 Dec 2024 09:03:18 +0900 Subject: [PATCH 02/32] [feat] add platform option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit llamaモデルは共通のオープンモデルなので、 どこで実行するかをオプションで決める必要がある const platformList = ["ollama", "groq", "replicate"] as const; --- index.ts | 2 +- lib/params.ts | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/index.ts b/index.ts index 1cfb097..a0970d4 100644 --- a/index.ts +++ b/index.ts @@ -27,7 +27,7 @@ import { getUserInputInMessage, readStdin } from "./lib/input.ts"; import { Params, parseArgs } from "./lib/params.ts"; import { Command, extractAtModel, isCommand } from "./lib/command.ts"; -const VERSION = "v0.7.0"; +const VERSION = "v0.7.0r"; const llmAsk = async (params: Params) => { params.debug && console.debug(params); diff --git a/lib/params.ts b/lib/params.ts index 1c20028..c4f945f 100644 --- a/lib/params.ts +++ b/lib/params.ts @@ -1,4 +1,16 @@ import { parse } from "https://deno.land/std/flags/mod.ts"; + +// Platformオプションとは +// llamaモデルは共通のオープンモデルなので、 +// どこで実行するかをオプションで決める必要がある +const platformList = ["ollama", "groq", "replicate"] as const; +type Platform = (typeof platformList)[number]; + +/** Platform型であることを保証する */ +function isPlatform(value: string): value is Platform { + return platformList.includes(value as Platform); +} + export type Params = { version: boolean; help: boolean; @@ -8,6 +20,7 @@ export type Params = { temperature: number; maxTokens: number; url?: string; + platform?: Platform; systemPrompt?: string; content?: string; }; @@ -37,12 +50,21 @@ export function parseArgs(): Params { "temperature", "x", "max-tokens", + "p", + "platform", ], default: { temperature: 1.0, "max-tokens": 1000, }, }); + + // platform の値を検証 + const platform = args.p || args.platform; + if (platform !== undefined && !isPlatform(platform)) { + throw new Error(`Platform must be one of: ${platformList.join(", ")}`); + } + return { // boolean option version: args.v || args.version || false, @@ -54,6 +76,7 @@ export function parseArgs(): Params { maxTokens: parseInt(String(args.x || args["max-tokens"])), temperature: parseFloat(String(args.t || args.temperature)), url: args.u || args.url || undefined, + platform: platform as Platform | undefined, systemPrompt: args.s || args["system-prompt"] || undefined, content: args._.length > 0 ? args._.join(" ") : undefined, // 残りの引数をすべてスペースで結合 }; From 22639e3271886923bc9f554f6372f418ce80eccd Mon Sep 17 00:00:00 2001 From: u1and0 Date: Mon, 23 Dec 2024 09:30:06 +0900 Subject: [PATCH 03/32] =?UTF-8?q?[feat]=20platform=20=E5=88=86=E5=B2=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/llm.ts | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/lib/llm.ts b/lib/llm.ts index a1b2621..3e70a96 100644 --- a/lib/llm.ts +++ b/lib/llm.ts @@ -50,7 +50,19 @@ export class LLM { const replicateModelPatterns = replicateModels.map((m: string) => new RegExp(m) ); - if (params.url !== undefined) { + + if (params.platform === "groq") { + return new ChatGroq({ + model: params.model, + temperature: params.temperature, + maxOutputTokens: params.maxTokens, + }); + } + + if (params.platform === "ollama") { + if (params.url === undefined) { + throw new Error("ollama needs URL parameter use --url"); + } // params.modelの文字列にollamaModelsのうちの一部が含まれていたらtrue // ollamaModelPatterns.some((p: RegExp) => p.test(params.model)) return new ChatOllama({ @@ -59,7 +71,9 @@ export class LLM { temperature: params.temperature, // maxTokens: params.maxTokens, // Not implemented yet on Langchain }); - } else if (params.model.startsWith("gpt")) { + } + + if (params.model.startsWith("gpt") || params.model) { return new ChatOpenAI({ modelName: params.model, temperature: params.temperature, @@ -77,12 +91,6 @@ export class LLM { temperature: params.temperature, maxOutputTokens: params.maxTokens, }); - } else if (params.model.startsWith("llama")) { - return new ChatGroq({ - model: params.model, - temperature: params.temperature, - maxOutputTokens: params.maxTokens, - }); } else if ( // params.modelの文字列にollamaModelsのうちの一部が含まれていたらtrue replicateModelPatterns.some((p: RegExp) => p.test(params.model)) && // replicateモデルのパターンに一致 From 1edbab2c415e36ae4a4b36b6aea3a3833a9e8051 Mon Sep 17 00:00:00 2001 From: u1and0 Date: Mon, 23 Dec 2024 08:38:26 +0900 Subject: [PATCH 04/32] =?UTF-8?q?[feat]=20LLM=20constructor()=20=E3=82=92?= =?UTF-8?q?=20llmConstructor()=E3=81=B8=E5=88=86=E5=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit model分岐 -> replicateModel判定 -> platform判定 の順序に変更 chat with groq success --- lib/llm.ts | 185 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 113 insertions(+), 72 deletions(-) diff --git a/lib/llm.ts b/lib/llm.ts index 3e70a96..60710a9 100644 --- a/lib/llm.ts +++ b/lib/llm.ts @@ -32,75 +32,7 @@ export class LLM { | undefined; constructor(private readonly params: Params) { - this.transrator = (() => { - const replicateModels = [ - "llama", - "mistral", - "command-r", - "llava", - "mixtral", - "deepseek", - "phi", - "hermes", - "orca", - "falcon", - "dolphin", - "gemma", - ]; - const replicateModelPatterns = replicateModels.map((m: string) => - new RegExp(m) - ); - - if (params.platform === "groq") { - return new ChatGroq({ - model: params.model, - temperature: params.temperature, - maxOutputTokens: params.maxTokens, - }); - } - - if (params.platform === "ollama") { - if (params.url === undefined) { - throw new Error("ollama needs URL parameter use --url"); - } - // params.modelの文字列にollamaModelsのうちの一部が含まれていたらtrue - // ollamaModelPatterns.some((p: RegExp) => p.test(params.model)) - return new ChatOllama({ - baseUrl: params.url, // http://yourIP:11434 - model: params.model, // "llama2:7b-chat", codellama:13b-fast-instruct, elyza:13b-fast-instruct ... - temperature: params.temperature, - // maxTokens: params.maxTokens, // Not implemented yet on Langchain - }); - } - - if (params.model.startsWith("gpt") || params.model) { - return new ChatOpenAI({ - modelName: params.model, - temperature: params.temperature, - maxTokens: params.maxTokens, - }); - } else if (params.model.startsWith("claude")) { - return new ChatAnthropic({ - modelName: params.model, - temperature: params.temperature, - maxTokens: params.maxTokens, - }); - } else if (params.model.startsWith("gemini")) { - return new ChatGoogleGenerativeAI({ - model: params.model, - temperature: params.temperature, - maxOutputTokens: params.maxTokens, - }); - } else if ( - // params.modelの文字列にollamaModelsのうちの一部が含まれていたらtrue - replicateModelPatterns.some((p: RegExp) => p.test(params.model)) && // replicateモデルのパターンに一致 - (params.model as Model) === params.model // Model型に一致 - ) { - return new Replicate(); - } else { - throw new Error(`model not found "${params.model}"`); - } - })(); + this.transrator = llmConstructor(params); } /** AI へ一回限りの質問をし、回答を出力して終了する */ @@ -202,7 +134,7 @@ export class LLM { * * See also test/llm_test.ts */ -export function generatePrompt(messages: Message[]): string { +function generatePrompt(messages: Message[]): string { // SystemMessageを取得 const sys = messages.find((m: Message) => m instanceof SystemMessage); const systemPrompt = `<> @@ -218,7 +150,7 @@ ${sys?.content ?? "You are helpful assistant."} }; // HumanMessageは[INST][/INST] で囲む // AIMessageは何もしない - const generatePrompt = (messages: (AIMessage | HumanMessage)[]): string => { + const surroundINST = (messages: (AIMessage | HumanMessage)[]): string => { return messages.map((message: AIMessage | HumanMessage, index: number) => { if (index === 0) { return `${message.content} [/INST]`; @@ -230,7 +162,7 @@ ${sys?.content ?? "You are helpful assistant."} }) .join("\n"); }; - const humanAIPrompt = generatePrompt(humanAIMessages); + const humanAIPrompt = surroundINST(humanAIMessages); return `[INST] ${systemPrompt}${humanAIPrompt}`; } @@ -247,3 +179,112 @@ async function* streamEncoder( yield str; } } + +/** LLM クラスのtransratorプロパティをparamsから判定し、 + * LLM インスタンスを生成して返す。 + * @param{Params} params - command line arguments parsed by parseArgs() + * @return : LLM model + * @throws{Error} model not found "${params.model}" + */ +function llmConstructor(params: Params): + | ChatOpenAI + | ChatAnthropic + | ChatOllama + | ChatGoogleGenerativeAI + | ChatGroq + | Replicate + | undefined { + // platform === undefined + if (params.model.startsWith("gpt")) { + return new ChatOpenAI({ + modelName: params.model, + temperature: params.temperature, + maxTokens: params.maxTokens, + }); + } else if (params.model.startsWith("claude")) { + return new ChatAnthropic({ + modelName: params.model, + temperature: params.temperature, + maxTokens: params.maxTokens, + }); + } else if (params.model.startsWith("gemini")) { + return new ChatGoogleGenerativeAI({ + model: params.model, + temperature: params.temperature, + maxOutputTokens: params.maxTokens, + }); + } else if (openModelMatch(params.model)) { + // platformを判定 + // llamaなどのオープンモデルはモデル名ではなく、 + // platform名で判定する + switch (params.platform) { + case undefined: { + throw new Error( + "open model needs platform parameter like `--platform=ollama`", + ); + } + case "groq": { + return new ChatGroq({ + model: params.model, + temperature: params.temperature, + maxTokens: params.maxTokens, + }); + } + case "ollama": { + // ollamaの場合は、ollamaが動作するサーバーのbaseUrlが必須 + if (params.url === undefined) { + throw new Error( + "ollama needs URL parameter with `--url http://your.host:11434`", + ); + } + // params.modelの文字列にollamaModelsのうちの一部が含まれていたらtrue + // ollamaModelPatterns.some((p: RegExp) => p.test(params.model)) + return new ChatOllama({ + baseUrl: params.url, // http://yourIP:11434 + model: params.model, // "llama2:7b-chat", codellama:13b-fast-instruct, elyza:13b-fast-instruct ... + temperature: params.temperature, + // maxTokens: params.maxTokens, // Not implemented yet on Langchain + }); + } + case "replicate": { + // params.modelの文字列にreplicateModelsのうちの一部が含まれていたらtrue + if ( + // replicateモデルのパターンに一致 + openModelMatch(params.model) && + // Model型に一致 + (params.model as Model) === params.model + ) { + return new Replicate(); + } else { + throw new Error(`model not found "${params.model}"`); + } + } + } + } else { + // not match any if-else if + throw new Error(`model not found "${params.model}"`); + } +} + +/** replicateモデルのパターンに一致したらtrue */ +const openModelMatch = (model: string): boolean => { + const replicateModels = [ + "llama", + "mistral", + "command-r", + "llava", + "mixtral", + "deepseek", + "phi", + "hermes", + "orca", + "falcon", + "dolphin", + "gemma", + ]; + const replicateModelPatterns = replicateModels.map((m: string) => + new RegExp(m) + ); + + return replicateModelPatterns.some((p: RegExp) => p.test(model)); +}; From 34ac02a3e78312f89841a819dffc230acf3abad4 Mon Sep 17 00:00:00 2001 From: u1and0 Date: Mon, 23 Dec 2024 08:49:41 +0900 Subject: [PATCH 05/32] [docs] update help message & README --- README.md | 7 +++++++ lib/help.ts | 3 +++ lib/params.ts | 2 +- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4158188..b92e395 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,7 @@ $ gpt -m gpt-4o-mini -x 1000 -t 1.0 [OPTIONS] PROMPT | -x | --max\_tokens | number | Number of AI answer tokens (default 1000) | | -t | --temperature | number | Higher number means more creative answers, lower number means more exact answers (default 1.0) | | -u | --url | string | URL and port number for ollama server | +| -p | --platform | string | Platform choose from ollama, groq, replicate | | -s | --system-prompt | string | The first instruction given to guide the AI model's response. | | -n | --no-chat | boolean | No chat mode. Just one time question and answer. | @@ -172,6 +173,12 @@ A Questions for Model - phi3 - llama3:70b - mixtral:8x7b-text-v0.1-q5\_K\_M... +- [Groq](https://console.groq.com/docs/models) ** Using before "$ ollama serve" locally ** + - llama3-groq-70b-8192-tool-use-preview + - llama-3.3-70b-specdec + - llama3.1-70b-specdec + - llama-3.2-1b-preview + - llama-3.2-3b-preview ## / command Help (/commands): diff --git a/lib/help.ts b/lib/help.ts index 23ab665..1dc78d9 100644 --- a/lib/help.ts +++ b/lib/help.ts @@ -1,3 +1,5 @@ +import { platformList } from "./params.ts"; + export const commandMessage = ` Help: /?, /help Help for a command @@ -20,6 +22,7 @@ export const helpMessage = -x, --max-tokens: number Number of AI answer tokens (default 1000) -t, --temperature: number Higher number means more creative answers, lower number means more exact answers (default 1.0) -u, --url: string URL and port number for ollama server + -p, --platform: string Platform choose from ${platformList.join(", ")} -s, --system-prompt: string The first instruction given to guide the AI model's response -n, --no-chat: boolean No chat mode. Just one time question and answer. PROMPT: diff --git a/lib/params.ts b/lib/params.ts index c4f945f..3789790 100644 --- a/lib/params.ts +++ b/lib/params.ts @@ -3,7 +3,7 @@ import { parse } from "https://deno.land/std/flags/mod.ts"; // Platformオプションとは // llamaモデルは共通のオープンモデルなので、 // どこで実行するかをオプションで決める必要がある -const platformList = ["ollama", "groq", "replicate"] as const; +export const platformList = ["ollama", "groq", "replicate"] as const; type Platform = (typeof platformList)[number]; /** Platform型であることを保証する */ From 457e56173c6300d8bd780c99dfe51368baa1abdd Mon Sep 17 00:00:00 2001 From: u1and0 Date: Mon, 23 Dec 2024 09:20:01 +0900 Subject: [PATCH 06/32] =?UTF-8?q?[fix]=20replicate=20model=E3=81=AE?= =?UTF-8?q?=E5=BD=A2=E5=BC=8F=E3=82=92=E5=9E=8B=E3=82=AC=E3=83=BC=E3=83=89?= =?UTF-8?q?=E3=82=92=E4=BD=BF=E3=81=A3=E3=81=A6=E3=83=9F=E3=82=B9=E3=82=92?= =?UTF-8?q?=E9=98=B2=E3=81=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /** Model型であることを保証する */ const isModel = (value: string): value is Model => { return value.includes("/") && value.split("/").length === 2; }; を使ってfalseの場合、 `Invalid reference to model version: "${params.model}". Expected format: owner/name or owner/name:version `, が吐き出される --- index.ts | 2 ++ lib/llm.ts | 21 +++++++++++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/index.ts b/index.ts index a0970d4..afa829c 100644 --- a/index.ts +++ b/index.ts @@ -47,6 +47,8 @@ const llmAsk = async (params: Params) => { return; } + // 灰色のテキスト + console.info(`\x1b[90m${commandMessage}\x1b[0m`); // 対話的回答 while (true) { // ユーザーからの入力待ち diff --git a/lib/llm.ts b/lib/llm.ts index 60710a9..e4c2459 100644 --- a/lib/llm.ts +++ b/lib/llm.ts @@ -17,8 +17,17 @@ import { Params } from "./params.ts"; /** AIMessage */ export type Message = AIMessage | HumanMessage | SystemMessage | never; //{ role: Role; content: string }; + +/** replicateで使うモデルは以下の形式 + * owner/name or owner/name:version + */ type Model = `${string}/${string}`; +/** Model型であることを保証する */ +const isModel = (value: string): value is Model => { + return value.includes("/") && value.split("/").length === 2; +}; + /** Chatインスタンスを作成する * @param: Params - LLMのパラメータ、モデル */ export class LLM { @@ -247,16 +256,12 @@ function llmConstructor(params: Params): }); } case "replicate": { - // params.modelの文字列にreplicateModelsのうちの一部が含まれていたらtrue - if ( - // replicateモデルのパターンに一致 - openModelMatch(params.model) && - // Model型に一致 - (params.model as Model) === params.model - ) { + if (isModel(params.model)) { // Model型に一致 return new Replicate(); } else { - throw new Error(`model not found "${params.model}"`); + throw new Error( + `Invalid reference to model version: "${params.model}". Expected format: owner/name or owner/name:version `, + ); } } } From 8956c741750f93946b4611777fff543c1e62731e Mon Sep 17 00:00:00 2001 From: u1and0 Date: Mon, 23 Dec 2024 17:57:33 +0900 Subject: [PATCH 07/32] =?UTF-8?q?[refactor]=20handleSlashCommand()=20/comm?= =?UTF-8?q?and=E3=82=92=E6=89=B1=E3=81=86=E9=96=A2=E6=95=B0=E3=82=92comman?= =?UTF-8?q?d.ts=E3=81=AB=E5=AE=9A=E7=BE=A9=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- index.ts | 72 ++++++++++++++++++++++++++------------------------ lib/command.ts | 50 ++++++++++++++++++++++++++++++++--- lib/help.ts | 15 ++++++----- lib/input.ts | 40 +++++++++++++++++++++------- lib/llm.ts | 6 +++-- lib/params.ts | 5 ++-- 7 files changed, 131 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index b92e395..3b00894 100644 --- a/README.md +++ b/README.md @@ -191,7 +191,7 @@ Help (/commands): Help (@commands): Change model while asking. - @{ModelName} Change LLM model -- ex) @gemini-1.5-pro any prompt... +- ex) @gemini-1.5-pro your question... ## Test diff --git a/index.ts b/index.ts index afa829c..d1e5467 100644 --- a/index.ts +++ b/index.ts @@ -25,9 +25,31 @@ import { commandMessage, helpMessage } from "./lib/help.ts"; import { LLM, Message } from "./lib/llm.ts"; import { getUserInputInMessage, readStdin } from "./lib/input.ts"; import { Params, parseArgs } from "./lib/params.ts"; -import { Command, extractAtModel, isCommand } from "./lib/command.ts"; - -const VERSION = "v0.7.0r"; +import { + Command, + extractAtModel, + handleSlashCommand, + isCommand, + modelStack, +} from "./lib/command.ts"; + +const VERSION = "v1.0.0"; + +/** 灰色のテキストで表示 */ +function consoleInfoWithGrayText(s: string): void { + console.info(`\x1b[90m${s}\x1b[0m`); +} + +const isAtCommand = (humanMessage: unknown): boolean => { + if (!(humanMessage instanceof HumanMessage)) { + return false; + } + const content = humanMessage.content.toString(); + if (!content) { + return false; + } + return content.startsWith("@"); +}; const llmAsk = async (params: Params) => { params.debug && console.debug(params); @@ -47,54 +69,34 @@ const llmAsk = async (params: Params) => { return; } - // 灰色のテキスト - console.info(`\x1b[90m${commandMessage}\x1b[0m`); // 対話的回答 + consoleInfoWithGrayText(commandMessage); while (true) { // ユーザーからの入力待ち - let humanMessage = await getUserInputInMessage(messages); + let humanMessage: HumanMessage | Command = await getUserInputInMessage( + messages, + ); - /** /commandを実行する - * Help: ヘルプメッセージを出力する - * Clear: systemp promptを残してコンテキストを削除する - * Bye: コマンドを終了する - */ + // /commandを実行する if (isCommand(humanMessage)) { - switch (humanMessage) { - case Command.Help: { - console.log(commandMessage); - continue; // Slashコマンドを処理したら次のループへ - } - case Command.Clear: { - // system promptが設定されていれば、それを残してコンテキストクリア - console.log("Context clear successful"); - messages = params.systemPrompt - ? [new SystemMessage(params.systemPrompt)] - : []; - continue; // Slashコマンドを処理したら次のループへ - } - case Command.Bye: { - Deno.exit(0); - } - } - } else if (humanMessage?.content.toString().startsWith("@")) { + messages = handleSlashCommand(humanMessage, messages); + continue; + } else if (isAtCommand(humanMessage)) { // @Model名で始まるinput はllmモデルを再指定する const { model, message } = extractAtModel( humanMessage.content.toString(), ); // モデル名指定以外のプロンプトがなければ前のプロンプトを引き継ぐ。 - humanMessage = message ? message : messages.at(-2); + humanMessage = message || messages.at(-2) || new HumanMessage(""); + if (model) { params.model = model; llm = new LLM(params); } } - // 最後のメッセージがHumanMessageではない場合 // ユーザーからの問いを追加 - if (humanMessage) { - messages.push(humanMessage); - } + messages.push(humanMessage); // console.debug(messages); // AIからの回答を追加 const aiMessage = await llm.ask(messages); @@ -121,6 +123,8 @@ const main = async () => { Deno.exit(0); } + // modelStackに使用した最初のモデルを追加 + modelStack.push(params.model); // 標準入力をチェック const stdinContent: string | null = await readStdin(); if (stdinContent) { diff --git a/lib/command.ts b/lib/command.ts index 5521112..5decf39 100644 --- a/lib/command.ts +++ b/lib/command.ts @@ -1,9 +1,15 @@ -import { HumanMessage } from "npm:@langchain/core/messages"; +import { HumanMessage, SystemMessage } from "npm:@langchain/core/messages"; +import { commandMessage } from "./help.ts"; +import { Message } from "./llm.ts"; + +/** この会話で使用したLLM モデルの履歴 */ +export const modelStack: string[] = []; export type _Command = | "/help" | "/?" | "/clear" + | "/modelStack" | "/bye" | "/exit" | "/quit"; @@ -11,6 +17,7 @@ export type _Command = export enum Command { Help = "HELP", Clear = "CLEAR", + ModelStack = "MODELSTACK", Bye = "BYE", } @@ -20,18 +27,22 @@ export const isCommand = (value: unknown): value is Command => { }; // Commandに指定したいずれかの数値を返す -export const newSlashCommand = (input: string): Command | undefined => { +export const newSlashCommand = (input: string): Command => { const input0 = input.trim().split(/[\s\n\t]+/, 1)[0]; const commandMap: Record<_Command, Command> = { "/help": Command.Help, "/?": Command.Help, "/clear": Command.Clear, + "/modelStack": Command.ModelStack, "/bye": Command.Bye, "/exit": Command.Bye, "/quit": Command.Bye, }; - - return commandMap[input0 as _Command]; + const command = commandMap[input0 as _Command]; + if (!command) { + throw new Error(`Invalid command. ${input0}`); + } + return command; }; type ModelMessage = { model?: string; message?: HumanMessage }; @@ -48,3 +59,34 @@ export const extractAtModel = (input: string): ModelMessage => { const message = input1 ? new HumanMessage(input1) : undefined; return { model, message }; }; + +export function handleSlashCommand( + command: Command, + messages: Message[], +): Message[] { + switch (command) { + case Command.Help: { + console.log(commandMessage); + break; // Slashコマンドを処理したら次のループへ + } + case Command.Clear: { + console.log("Context clear successful"); + // SystemMessage 以外は捨てて新しい配列を返す + return messages.filter((message: Message) => { + if (message instanceof SystemMessage) { + return message; + } + }); + } + // 使用したモデルの履歴を表示する + case Command.ModelStack: { + console.log(`You were chat with them...\n${modelStack.join("\n")}`); + break; + } + case Command.Bye: { + Deno.exit(0); + } + } + // messagesをそのまま返す + return messages; +} diff --git a/lib/help.ts b/lib/help.ts index 1dc78d9..5572d97 100644 --- a/lib/help.ts +++ b/lib/help.ts @@ -1,12 +1,15 @@ import { platformList } from "./params.ts"; export const commandMessage = ` - Help: - /?, /help Help for a command - /clear Clear session context - /bye Exit - @{ModelName} Change LLM model - ex) @gemini-1.5-pro any prompt... +Ctrl+D to confirm input. + +Help: + /?, /help Help for a command + /clear Clear session context + /modelStack Show model's history + /bye,/exit,/quit Exit + @{ModelName} Change LLM model + ex) @gemini-1.5-pro your question... `; export const helpMessage = diff --git a/lib/input.ts b/lib/input.ts index 12df9c0..27c3cdd 100644 --- a/lib/input.ts +++ b/lib/input.ts @@ -3,24 +3,44 @@ import { HumanMessage } from "npm:@langchain/core/messages"; import { Message } from "./llm.ts"; import { Command, newSlashCommand } from "./command.ts"; -/** ユーザーの入力とシステムプロンプトをmessages内にセットする */ +/** ユーザーの入力を返す + * メッセージ配列から最後のユーザー入力を取得、もしくは新しいユーザー入力を待ち受ける + * + * - 最後のメッセージがユーザーからのものでない場合: ユーザーから新しい入力を取得 + * - スラッシュコマンドの場合: Command オブジェクト + * - 通常のメッセージの場合: HumanMessage オブジェクト + * - 最後のメッセージがユーザーからのものの場合: そのHumanMessageを返す + * + * @param {Message[]}: messages - 会話履歴のメッセージ配列 + * @returns {HumanMessage | Command} - ユーザーの入力、またはSlash Command + */ export async function getUserInputInMessage( messages: Message[], -): Promise { +): Promise { // 最後のMessageがユーザーからのメッセージではない場合、 // endlessInput()でユーザーからの質問を待ち受ける const lastMessage: Message | undefined = messages.at(-1); // console.debug(lastMessage); - if (!(lastMessage instanceof HumanMessage)) { - const input = await endlessInput(); - // / から始まる入力はコマンド解釈を試みる - if (input.trim().startsWith("/")) { - const cmd = newSlashCommand(input); - if (cmd) return cmd; - } + if (lastMessage instanceof HumanMessage) { + return lastMessage; + } + // 入力が何かあるまで入力を施す + const input: string = await endlessInput(); + + // / から始まらなければ、ユーザーの入力として返す + if (!input.trim().startsWith("/")) { + return new HumanMessage(input); + } + + // / から始まる入力はコマンド解釈を試みる + try { + const cmd = newSlashCommand(input); + return cmd; + } catch { + // Invalid command errorの場合は、 + // /を含めてHumanMessageとして返す return new HumanMessage(input); } - return; // console.debug(messages); } diff --git a/lib/llm.ts b/lib/llm.ts index e4c2459..80adfdf 100644 --- a/lib/llm.ts +++ b/lib/llm.ts @@ -24,8 +24,10 @@ export type Message = AIMessage | HumanMessage | SystemMessage | never; //{ role type Model = `${string}/${string}`; /** Model型であることを保証する */ -const isModel = (value: string): value is Model => { - return value.includes("/") && value.split("/").length === 2; +const isModel = (value: unknown): value is Model => { + return typeof value === "string" && + value.includes("/") && + value.split("/").length === 2; }; /** Chatインスタンスを作成する diff --git a/lib/params.ts b/lib/params.ts index 3789790..107c3ab 100644 --- a/lib/params.ts +++ b/lib/params.ts @@ -7,8 +7,9 @@ export const platformList = ["ollama", "groq", "replicate"] as const; type Platform = (typeof platformList)[number]; /** Platform型であることを保証する */ -function isPlatform(value: string): value is Platform { - return platformList.includes(value as Platform); +function isPlatform(value: unknown): value is Platform { + return typeof value === "string" && + platformList.includes(value as Platform); } export type Params = { From 6f3f54944bb2e95a64902515628aeef88720a828 Mon Sep 17 00:00:00 2001 From: u1and0 Date: Mon, 23 Dec 2024 18:10:31 +0900 Subject: [PATCH 08/32] =?UTF-8?q?[feat]=20=E4=BC=9A=E8=A9=B1=E9=80=94?= =?UTF-8?q?=E4=B8=AD=E3=81=AB@command=E3=81=A7=E3=83=A2=E3=83=87=E3=83=AB?= =?UTF-8?q?=E3=82=92=E5=88=87=E3=82=8A=E6=9B=BF=E3=81=88=E3=82=8B=E3=81=A8?= =?UTF-8?q?=E3=81=8D=E3=81=AB=E3=80=81=E8=AA=A4=E3=81=A3=E3=81=9F=E3=83=A2?= =?UTF-8?q?=E3=83=87=E3=83=AB=E3=82=92=E6=8C=87=E5=AE=9A=E3=81=97=E3=81=A6?= =?UTF-8?q?=E3=82=82=E8=90=BD=E3=81=A1=E3=81=AA=E3=81=84=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E3=81=97=E3=81=9F=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.ts | 28 ++++++++++++++-------------- lib/command.ts | 14 +++++++++++++- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/index.ts b/index.ts index d1e5467..46e043b 100644 --- a/index.ts +++ b/index.ts @@ -29,7 +29,8 @@ import { Command, extractAtModel, handleSlashCommand, - isCommand, + isAtCommand, + isSlashCommand, modelStack, } from "./lib/command.ts"; @@ -40,17 +41,6 @@ function consoleInfoWithGrayText(s: string): void { console.info(`\x1b[90m${s}\x1b[0m`); } -const isAtCommand = (humanMessage: unknown): boolean => { - if (!(humanMessage instanceof HumanMessage)) { - return false; - } - const content = humanMessage.content.toString(); - if (!content) { - return false; - } - return content.startsWith("@"); -}; - const llmAsk = async (params: Params) => { params.debug && console.debug(params); // 引数に従ったLLMインスタンスを作成 @@ -78,7 +68,7 @@ const llmAsk = async (params: Params) => { ); // /commandを実行する - if (isCommand(humanMessage)) { + if (isSlashCommand(humanMessage)) { messages = handleSlashCommand(humanMessage, messages); continue; } else if (isAtCommand(humanMessage)) { @@ -90,8 +80,18 @@ const llmAsk = async (params: Params) => { humanMessage = message || messages.at(-2) || new HumanMessage(""); if (model) { + const modelBackup = params.model; params.model = model; - llm = new LLM(params); + try { + llm = new LLM(params); + } catch (error: unknown) { + // Modelの解釈に失敗したらエラーを吐いて + // 前のモデルに戻す + console.error(error); + params.model = modelBackup; + continue; + } + modelStack.push(model); } } diff --git a/lib/command.ts b/lib/command.ts index 5decf39..708152e 100644 --- a/lib/command.ts +++ b/lib/command.ts @@ -22,7 +22,7 @@ export enum Command { } // Command 型の型ガード -export const isCommand = (value: unknown): value is Command => { +export const isSlashCommand = (value: unknown): value is Command => { return Object.values(Command).includes(value as Command); }; @@ -90,3 +90,15 @@ export function handleSlashCommand( // messagesをそのまま返す return messages; } + +/** @が最初につく場合を判定 */ +export const isAtCommand = (humanMessage: unknown): boolean => { + if (!(humanMessage instanceof HumanMessage)) { + return false; + } + const content = humanMessage.content.toString(); + if (!content) { + return false; + } + return content.startsWith("@"); +}; From a69c383d9a0c24e4e48581766475a3354e1613c0 Mon Sep 17 00:00:00 2001 From: u1and0 Date: Mon, 23 Dec 2024 18:42:08 +0900 Subject: [PATCH 09/32] =?UTF-8?q?[feat]=20o1,=20o1-mini=20model=E3=81=AB?= =?UTF-8?q?=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ただし、maxtokensパラメータにlangchainが対応していないため コメントアウト。 --- lib/llm.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/llm.ts b/lib/llm.ts index 80adfdf..11929ff 100644 --- a/lib/llm.ts +++ b/lib/llm.ts @@ -205,13 +205,18 @@ function llmConstructor(params: Params): | ChatGroq | Replicate | undefined { - // platform === undefined if (params.model.startsWith("gpt")) { return new ChatOpenAI({ modelName: params.model, temperature: params.temperature, maxTokens: params.maxTokens, }); + } else if (/^o[0-9]/.test(params.model)) { + return new ChatOpenAI({ + modelName: params.model, + temperature: params.temperature, + // max_completion_tokens: params.maxTokens, + }); } else if (params.model.startsWith("claude")) { return new ChatAnthropic({ modelName: params.model, From ec4e5dacb99f0b5cca90b4a1289c7e5fc861a5fc Mon Sep 17 00:00:00 2001 From: u1and0 Date: Mon, 23 Dec 2024 18:47:19 +0900 Subject: [PATCH 10/32] [docs] add o1 family at README, help message --- README.md | 5 ++++- lib/help.ts | 11 ++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3b00894..f36ab41 100644 --- a/README.md +++ b/README.md @@ -154,7 +154,10 @@ A Questions for Model ## Models - [OpenAI](https://platform.openai.com/docs/models) - gpt-4o-mini - - gpt-4o... + - gpt-4o + - o1 + - o1-preview + - o1-mini... - [Anthropic](https://docs.anthropic.com/claude/docs/models-overview) - claude-3-opus-20240229 - claude-3-haiku-20240307 diff --git a/lib/help.ts b/lib/help.ts index 5572d97..63a4e87 100644 --- a/lib/help.ts +++ b/lib/help.ts @@ -33,7 +33,10 @@ export const helpMessage = Models: - [OpenAI](https://platform.openai.com/docs/models) - gpt-4o-mini - - gpt-4o... + - gpt-4o + - o1 + - o1-preview + - o1-mini... - [Anthropic](https://docs.anthropic.com/claude/docs/models-overview) - claude-3-5-sonnet-20241022 - claude-3-5-sonnet-latest @@ -55,5 +58,11 @@ export const helpMessage = - phi3 - llama3:70b - mixtral:8x7b-text-v0.1-q5_K_M... + - [Groq](https://console.groq.com/docs/models) ** Using before "$ ollama serve" locally ** + - llama3-groq-70b-8192-tool-use-preview + - llama-3.3-70b-specdec + - llama3.1-70b-specdec + - llama-3.2-1b-preview + - llama-3.2-3b-preview ${commandMessage} `; From c49475397b5e5f2a2889a1b9b8a35e8cf8487bde Mon Sep 17 00:00:00 2001 From: u1and0 Date: Mon, 23 Dec 2024 19:25:45 +0900 Subject: [PATCH 11/32] =?UTF-8?q?[feat]=20Open=20model=E3=81=AE=E5=88=A4?= =?UTF-8?q?=E5=AE=9A=E3=82=92=E3=82=84=E3=82=81=E3=81=9F=20platform?= =?UTF-8?q?=E3=81=A7=E5=88=A4=E6=96=AD=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 8 +++++++- lib/help.ts | 6 ++++++ lib/llm.ts | 42 ++++++++++++------------------------------ lib/params.ts | 9 +++++++-- 4 files changed, 32 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index f36ab41..a663119 100644 --- a/README.md +++ b/README.md @@ -181,7 +181,13 @@ A Questions for Model - llama-3.3-70b-specdec - llama3.1-70b-specdec - llama-3.2-1b-preview - - llama-3.2-3b-preview + - llama-3.2-3b-preview... +- [TogetherAI](https://api.together.ai/models) ** Using before "$ ollama serve" locally ** + - meta-llama/Llama-3.3-70B-Instruct-Turbo + - Qwen/QwQ-32B-Preview + - meta-llama/Llama-3.1-405B-Instruct-Turbo + - google/gemma-2-27b-it + - mistralai/Mistral-7B-Instruct-v0.3... ## / command Help (/commands): diff --git a/lib/help.ts b/lib/help.ts index 63a4e87..c565918 100644 --- a/lib/help.ts +++ b/lib/help.ts @@ -64,5 +64,11 @@ export const helpMessage = - llama3.1-70b-specdec - llama-3.2-1b-preview - llama-3.2-3b-preview + - [TogetherAI](https://api.together.ai/models) ** Using before "$ ollama serve" locally ** + - meta-llama/Llama-3.3-70B-Instruct-Turbo + - Qwen/QwQ-32B-Preview + - meta-llama/Llama-3.1-405B-Instruct-Turbo + - google/gemma-2-27b-it + - mistralai/Mistral-7B-Instruct-v0.3... ${commandMessage} `; diff --git a/lib/llm.ts b/lib/llm.ts index 11929ff..f93fe79 100644 --- a/lib/llm.ts +++ b/lib/llm.ts @@ -3,6 +3,7 @@ import { ChatAnthropic } from "npm:@langchain/anthropic"; import { ChatOllama } from "npm:@langchain/community/chat_models/ollama"; import { ChatGoogleGenerativeAI } from "npm:@langchain/google-genai"; import { ChatGroq } from "npm:@langchain/groq"; +import { ChatTogetherAI } from "npm:@langchain/community/chat_models/togetherai"; import Replicate from "npm:replicate"; import ServerSentEvent from "npm:replicate"; import { @@ -203,6 +204,7 @@ function llmConstructor(params: Params): | ChatOllama | ChatGoogleGenerativeAI | ChatGroq + | ChatTogetherAI | Replicate | undefined { if (params.model.startsWith("gpt")) { @@ -229,10 +231,9 @@ function llmConstructor(params: Params): temperature: params.temperature, maxOutputTokens: params.maxTokens, }); - } else if (openModelMatch(params.model)) { - // platformを判定 - // llamaなどのオープンモデルはモデル名ではなく、 - // platform名で判定する + } else { + // それ以外のモデルはオープンモデルとして platformを判定 + // llamaなどのオープンモデルはモデル名ではなく、 platform名で判定する switch (params.platform) { case undefined: { throw new Error( @@ -246,6 +247,13 @@ function llmConstructor(params: Params): maxTokens: params.maxTokens, }); } + case "togetherai": { + return new ChatTogetherAI({ + model: params.model, + temperature: params.temperature, + maxTokens: params.maxTokens, + }); + } case "ollama": { // ollamaの場合は、ollamaが動作するサーバーのbaseUrlが必須 if (params.url === undefined) { @@ -272,31 +280,5 @@ function llmConstructor(params: Params): } } } - } else { - // not match any if-else if - throw new Error(`model not found "${params.model}"`); } } - -/** replicateモデルのパターンに一致したらtrue */ -const openModelMatch = (model: string): boolean => { - const replicateModels = [ - "llama", - "mistral", - "command-r", - "llava", - "mixtral", - "deepseek", - "phi", - "hermes", - "orca", - "falcon", - "dolphin", - "gemma", - ]; - const replicateModelPatterns = replicateModels.map((m: string) => - new RegExp(m) - ); - - return replicateModelPatterns.some((p: RegExp) => p.test(model)); -}; diff --git a/lib/params.ts b/lib/params.ts index 107c3ab..b221ef7 100644 --- a/lib/params.ts +++ b/lib/params.ts @@ -1,9 +1,14 @@ import { parse } from "https://deno.land/std/flags/mod.ts"; -// Platformオプションとは +// Platformオプション // llamaモデルは共通のオープンモデルなので、 // どこで実行するかをオプションで決める必要がある -export const platformList = ["ollama", "groq", "replicate"] as const; +export const platformList = [ + "ollama", + "groq", + "togetherai", + "replicate", +] as const; type Platform = (typeof platformList)[number]; /** Platform型であることを保証する */ From 169a9cc1adf233776fb1f105c2461eb9a5f6621a Mon Sep 17 00:00:00 2001 From: u1and0 Date: Mon, 23 Dec 2024 21:56:31 +0900 Subject: [PATCH 12/32] =?UTF-8?q?[refactor]=20if-else=20=E3=81=8B=E3=82=89?= =?UTF-8?q?=20map-type=20=E3=81=AE=E5=88=86=E5=B2=90=E3=81=AB=E5=A4=89?= =?UTF-8?q?=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/llm.ts | 185 +++++++++++++++++++++++++++++++---------------------- 1 file changed, 110 insertions(+), 75 deletions(-) diff --git a/lib/llm.ts b/lib/llm.ts index f93fe79..b8eaff0 100644 --- a/lib/llm.ts +++ b/lib/llm.ts @@ -192,6 +192,17 @@ async function* streamEncoder( } } +type ModelMap = { + [key: string]: (params: Params) => + | ChatOpenAI + | ChatAnthropic + | ChatOllama + | ChatGoogleGenerativeAI + | ChatGroq + | ChatTogetherAI + | Replicate; +}; + /** LLM クラスのtransratorプロパティをparamsから判定し、 * LLM インスタンスを生成して返す。 * @param{Params} params - command line arguments parsed by parseArgs() @@ -205,80 +216,104 @@ function llmConstructor(params: Params): | ChatGoogleGenerativeAI | ChatGroq | ChatTogetherAI - | Replicate - | undefined { - if (params.model.startsWith("gpt")) { - return new ChatOpenAI({ - modelName: params.model, - temperature: params.temperature, - maxTokens: params.maxTokens, - }); - } else if (/^o[0-9]/.test(params.model)) { - return new ChatOpenAI({ - modelName: params.model, - temperature: params.temperature, - // max_completion_tokens: params.maxTokens, - }); - } else if (params.model.startsWith("claude")) { - return new ChatAnthropic({ - modelName: params.model, - temperature: params.temperature, - maxTokens: params.maxTokens, - }); - } else if (params.model.startsWith("gemini")) { - return new ChatGoogleGenerativeAI({ - model: params.model, - temperature: params.temperature, - maxOutputTokens: params.maxTokens, - }); - } else { - // それ以外のモデルはオープンモデルとして platformを判定 - // llamaなどのオープンモデルはモデル名ではなく、 platform名で判定する - switch (params.platform) { - case undefined: { - throw new Error( - "open model needs platform parameter like `--platform=ollama`", - ); - } - case "groq": { - return new ChatGroq({ - model: params.model, - temperature: params.temperature, - maxTokens: params.maxTokens, - }); - } - case "togetherai": { - return new ChatTogetherAI({ - model: params.model, - temperature: params.temperature, - maxTokens: params.maxTokens, - }); - } - case "ollama": { - // ollamaの場合は、ollamaが動作するサーバーのbaseUrlが必須 - if (params.url === undefined) { - throw new Error( - "ollama needs URL parameter with `--url http://your.host:11434`", - ); - } - // params.modelの文字列にollamaModelsのうちの一部が含まれていたらtrue - // ollamaModelPatterns.some((p: RegExp) => p.test(params.model)) - return new ChatOllama({ - baseUrl: params.url, // http://yourIP:11434 - model: params.model, // "llama2:7b-chat", codellama:13b-fast-instruct, elyza:13b-fast-instruct ... - temperature: params.temperature, - // maxTokens: params.maxTokens, // Not implemented yet on Langchain - }); - } - case "replicate": { - if (isModel(params.model)) { // Model型に一致 - return new Replicate(); - } else { - throw new Error( - `Invalid reference to model version: "${params.model}". Expected format: owner/name or owner/name:version `, - ); - } - } - } + | Replicate { + const modelMap: ModelMap = { + "^gpt": createOpenAIInstance, + "^o[0-9]": createOpenAIOModelINstance, + "^claude": createAnthropicInstance, + "^gemini": createGoogleGenerativeAIInstance, + // ... + }; + + const createInstance = Object.keys(modelMap).find((regex) => + new RegExp(regex).test(params.model) + ); + if (createInstance === undefined) { + throw new Error(`unknown model ${params.model}`); } + return modelMap[createInstance](params); } + +// } else { +// // それ以外のモデルはオープンモデルとして platformを判定 +// // llamaなどのオープンモデルはモデル名ではなく、 platform名で判定する +// switch (params.platform) { +// case undefined: { +// throw new Error( +// "open model needs platform parameter like `--platform=ollama`", +// ); +// } +// case "groq": { +// return new ChatGroq({ +// model: params.model, +// temperature: params.temperature, +// maxTokens: params.maxTokens, +// }); +// } +// case "togetherai": { +// return new ChatTogetherAI({ +// model: params.model, +// temperature: params.temperature, +// maxTokens: params.maxTokens, +// }); +// } +// case "ollama": { +// // ollamaの場合は、ollamaが動作するサーバーのbaseUrlが必須 +// if (params.url === undefined) { +// throw new Error( +// "ollama needs URL parameter with `--url http://your.host:11434`", +// ); +// } +// // params.modelの文字列にollamaModelsのうちの一部が含まれていたらtrue +// // ollamaModelPatterns.some((p: RegExp) => p.test(params.model)) +// return new ChatOllama({ +// baseUrl: params.url, // http://yourIP:11434 +// model: params.model, // "llama2:7b-chat", codellama:13b-fast-instruct, elyza:13b-fast-instruct ... +// temperature: params.temperature, +// // maxTokens: params.maxTokens, // Not implemented yet on Langchain +// }); +// } +// case "replicate": { +// if (isModel(params.model)) { // Model型に一致 +// return new Replicate(); +// } else { +// throw new Error( +// `Invalid reference to model version: "${params.model}". Expected format: owner/name or owner/name:version `, +// ); +// } +// } +// } +// } +// } + +const createOpenAIInstance = (params: Params): ChatOpenAI => { + return new ChatOpenAI({ + modelName: params.model, + temperature: params.temperature, + maxTokens: params.maxTokens, + }); +}; + +const createOpenAIOModelINstance = (params: Params) => { + return new ChatOpenAI({ + modelName: params.model, + temperature: params.temperature, + // max_completion_tokens: params.maxTokens, + }); +}; + +const createAnthropicInstance = (params: Params) => { + return new ChatAnthropic({ + modelName: params.model, + temperature: params.temperature, + maxTokens: params.maxTokens, + }); +}; + +const createGoogleGenerativeAIInstance = (params: Params) => { + return new ChatGoogleGenerativeAI({ + model: params.model, + temperature: params.temperature, + maxOutputTokens: params.maxTokens, + }); +}; From 83996da8d6bce55d4cb644847f3f2824aaa62a82 Mon Sep 17 00:00:00 2001 From: u1and0 Date: Mon, 23 Dec 2024 22:01:48 +0900 Subject: [PATCH 13/32] [refactor] groq instance --- lib/llm.ts | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/lib/llm.ts b/lib/llm.ts index b8eaff0..4eba3d9 100644 --- a/lib/llm.ts +++ b/lib/llm.ts @@ -225,13 +225,24 @@ function llmConstructor(params: Params): // ... }; + const platformMap: PlatfromMap = { + "groq": createGroqInstance, + }; + const createInstance = Object.keys(modelMap).find((regex) => new RegExp(regex).test(params.model) ); - if (createInstance === undefined) { - throw new Error(`unknown model ${params.model}`); + + if (createInstance !== undefined) { + return modelMap[createInstance](params); + } + + const createInstanceFromPlatform = platformMap[params.platform]; + if (createInstanceFromPlatform !== undefined) { + return platformMap[createInstanceFromPlatform](params); } - return modelMap[createInstance](params); + + throw new Error(`unknown model ${params.model}`); } // } else { @@ -244,11 +255,6 @@ function llmConstructor(params: Params): // ); // } // case "groq": { -// return new ChatGroq({ -// model: params.model, -// temperature: params.temperature, -// maxTokens: params.maxTokens, -// }); // } // case "togetherai": { // return new ChatTogetherAI({ @@ -317,3 +323,11 @@ const createGoogleGenerativeAIInstance = (params: Params) => { maxOutputTokens: params.maxTokens, }); }; + +const createGroqInstance = (params: Params) => { + return new ChatGroq({ + model: params.model, + temperature: params.temperature, + maxTokens: params.maxTokens, + }); +}; From 8690f38725366efdb03c970784662820d9486a38 Mon Sep 17 00:00:00 2001 From: u1and0 Date: Wed, 25 Dec 2024 01:39:07 +0900 Subject: [PATCH 14/32] [mv] test [chore] deno.json moduleResolution auto --- deno.json | 7 ++++++- lib/command_test.ts | 30 ------------------------------ {lib => test}/params_test.ts | 0 3 files changed, 6 insertions(+), 31 deletions(-) delete mode 100644 lib/command_test.ts rename {lib => test}/params_test.ts (100%) diff --git a/deno.json b/deno.json index 22249e3..6717cd9 100644 --- a/deno.json +++ b/deno.json @@ -4,5 +4,10 @@ }, "test": { "exclude": ["node_modules/"] - } + }, + "dependencies": { + "@langchain/groq": "^0.1.2" + }, + "nodeModulesDir": "auto" } + diff --git a/lib/command_test.ts b/lib/command_test.ts deleted file mode 100644 index 848672c..0000000 --- a/lib/command_test.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { assertEquals } from "https://deno.land/std@0.224.0/assert/assert_equals.ts"; -import { Command, extractAtModel, newSlashCommand } from "../lib/command.ts"; - -Deno.test("SlashCommand constructor", () => { - const testCases: [string, Command | string][] = [ - ["/help", "HELP"], - ["/help my content", "HELP"], // multi word test - ["/help\nmy content", "HELP"], // \n trim test - [" /help my content", "HELP"], // trim() test - ["/help\tmy content", "HELP"], // \t trim test - ]; - - for (const [args, expected] of testCases) { - const actual = newSlashCommand(args); - assertEquals(actual, expected); - } -}); - -Deno.test("ユーザーの入力が@から始まると、@に続くモデル名を返す", () => { - const testCases: [string, string | undefined][] = [ - ["@modelName arg1 arg2", "modelName"], // 行頭に@が入るとモデル名を返す - [" @modelName arg1 arg2", undefined], // 行頭にスペースが入ると@コマンドではない - ["plain text", undefined], - ]; - - for (const [args, expected] of testCases) { - const actual = extractAtModel(args); - assertEquals(actual.model, expected); - } -}); diff --git a/lib/params_test.ts b/test/params_test.ts similarity index 100% rename from lib/params_test.ts rename to test/params_test.ts From 01f65522757081bcf32ce9e9705e40dbb057171b Mon Sep 17 00:00:00 2001 From: u1and0 Date: Thu, 26 Dec 2024 00:55:12 +0900 Subject: [PATCH 15/32] [refactor] llm switch -> map-interface --- index.ts | 6 +- lib/help.ts | 2 +- lib/llm.ts | 157 ++++++++++++++++++++++++++------------------ lib/params.ts | 24 ++----- test/llm_test.ts | 8 ++- test/params_test.ts | 5 ++ 6 files changed, 115 insertions(+), 87 deletions(-) diff --git a/index.ts b/index.ts index 46e043b..7f117f7 100644 --- a/index.ts +++ b/index.ts @@ -79,14 +79,16 @@ const llmAsk = async (params: Params) => { // モデル名指定以外のプロンプトがなければ前のプロンプトを引き継ぐ。 humanMessage = message || messages.at(-2) || new HumanMessage(""); + // @コマンドで指定したモデルのパースに成功したら + // モデルスタックに追加して新しいモデルで会話を始める。 + // パースに失敗したら、以前のモデルを復元してエラー表示して + // 前のモデルに戻して会話を継続。 if (model) { const modelBackup = params.model; params.model = model; try { llm = new LLM(params); } catch (error: unknown) { - // Modelの解釈に失敗したらエラーを吐いて - // 前のモデルに戻す console.error(error); params.model = modelBackup; continue; diff --git a/lib/help.ts b/lib/help.ts index c565918..9dc4cdd 100644 --- a/lib/help.ts +++ b/lib/help.ts @@ -1,4 +1,4 @@ -import { platformList } from "./params.ts"; +import { platformList } from "./llm.ts"; export const commandMessage = ` Ctrl+D to confirm input. diff --git a/lib/llm.ts b/lib/llm.ts index 4eba3d9..d29da28 100644 --- a/lib/llm.ts +++ b/lib/llm.ts @@ -146,7 +146,7 @@ export class LLM { * * See also test/llm_test.ts */ -function generatePrompt(messages: Message[]): string { +export function generatePrompt(messages: Message[]): string { // SystemMessageを取得 const sys = messages.find((m: Message) => m instanceof SystemMessage); const systemPrompt = `<> @@ -193,14 +193,34 @@ async function* streamEncoder( } type ModelMap = { - [key: string]: (params: Params) => - | ChatOpenAI - | ChatAnthropic - | ChatOllama - | ChatGoogleGenerativeAI - | ChatGroq - | ChatTogetherAI - | Replicate; + [key: string]: ( + params: Params, + ) => ChatOpenAI | ChatAnthropic | ChatGoogleGenerativeAI; +}; + +// Platformオプション +// llamaモデルは共通のオープンモデルなので、 +// どこで実行するかをオプションで決める必要がある +export const platformList = [ + "ollama", + "groq", + "togetherai", + "replicate", +] as const; + +export type Platform = (typeof platformList)[number]; + +/** Platform型であることを保証する */ +export function isPlatform(value: unknown): value is Platform { + return typeof value === "string" && + platformList.includes(value as Platform); +} + +/** Platformごとに返すモデルのインスタンスを返す関数 */ +type PlatformMap = { + [key in Platform]: ( + params: Params, + ) => ChatGroq | ChatTogetherAI | ChatOllama | Replicate; }; /** LLM クラスのtransratorプロパティをparamsから判定し、 @@ -212,86 +232,60 @@ type ModelMap = { function llmConstructor(params: Params): | ChatOpenAI | ChatAnthropic - | ChatOllama | ChatGoogleGenerativeAI | ChatGroq | ChatTogetherAI + | ChatOllama | Replicate { const modelMap: ModelMap = { "^gpt": createOpenAIInstance, "^o[0-9]": createOpenAIOModelINstance, "^claude": createAnthropicInstance, "^gemini": createGoogleGenerativeAIInstance, - // ... - }; + } as const; - const platformMap: PlatfromMap = { + const platformMap: PlatformMap = { "groq": createGroqInstance, - }; + "togetherai": createTogetherAIInstance, + "ollama": createOllamaInstance, + "replicate": createReplicateInstance, + } as const; + // Closed modelがインスタンス化できるか + // 正規表現でマッチング const createInstance = Object.keys(modelMap).find((regex) => new RegExp(regex).test(params.model) ); + // Closed modelが見つかればそれをインスタンス化して返す if (createInstance !== undefined) { return modelMap[createInstance](params); } + // Closed modelでマッチするモデルが見つからなかった場合、 + // Open model がインスタンス化できるか。 + // + // llamaなどのオープンモデルはモデル名ではなく、 + // platform名で判定する + // + // platformがオプションに指定されていなければエラー + if (params.platform === undefined) { + throw new Error( + `You must choose one of these Platforms : --platform=${ + platformList.join(", ") + }`, + ); + } + + // platformMap からオプションに指定したものがなければエラー const createInstanceFromPlatform = platformMap[params.platform]; - if (createInstanceFromPlatform !== undefined) { - return platformMap[createInstanceFromPlatform](params); + if (createInstanceFromPlatform === undefined) { + throw new Error(`unknown model ${params.model}`); } - throw new Error(`unknown model ${params.model}`); + return createInstanceFromPlatform(params); } -// } else { -// // それ以外のモデルはオープンモデルとして platformを判定 -// // llamaなどのオープンモデルはモデル名ではなく、 platform名で判定する -// switch (params.platform) { -// case undefined: { -// throw new Error( -// "open model needs platform parameter like `--platform=ollama`", -// ); -// } -// case "groq": { -// } -// case "togetherai": { -// return new ChatTogetherAI({ -// model: params.model, -// temperature: params.temperature, -// maxTokens: params.maxTokens, -// }); -// } -// case "ollama": { -// // ollamaの場合は、ollamaが動作するサーバーのbaseUrlが必須 -// if (params.url === undefined) { -// throw new Error( -// "ollama needs URL parameter with `--url http://your.host:11434`", -// ); -// } -// // params.modelの文字列にollamaModelsのうちの一部が含まれていたらtrue -// // ollamaModelPatterns.some((p: RegExp) => p.test(params.model)) -// return new ChatOllama({ -// baseUrl: params.url, // http://yourIP:11434 -// model: params.model, // "llama2:7b-chat", codellama:13b-fast-instruct, elyza:13b-fast-instruct ... -// temperature: params.temperature, -// // maxTokens: params.maxTokens, // Not implemented yet on Langchain -// }); -// } -// case "replicate": { -// if (isModel(params.model)) { // Model型に一致 -// return new Replicate(); -// } else { -// throw new Error( -// `Invalid reference to model version: "${params.model}". Expected format: owner/name or owner/name:version `, -// ); -// } -// } -// } -// } -// } - const createOpenAIInstance = (params: Params): ChatOpenAI => { return new ChatOpenAI({ modelName: params.model, @@ -324,10 +318,43 @@ const createGoogleGenerativeAIInstance = (params: Params) => { }); }; -const createGroqInstance = (params: Params) => { +const createGroqInstance = (params: Params): ChatGroq => { return new ChatGroq({ model: params.model, temperature: params.temperature, maxTokens: params.maxTokens, }); }; + +const createTogetherAIInstance = (params: Params): ChatTogetherAI => { + return new ChatTogetherAI({ + model: params.model, + temperature: params.temperature, + maxTokens: params.maxTokens, + }); +}; + +const createOllamaInstance = (params: Params): ChatOllama => { + // ollamaの場合は、ollamaが動作するサーバーのbaseUrlが必須 + if (params.url === undefined) { + throw new Error( + "ollama needs URL parameter with `--url http://your.host:11434`", + ); + } + return new ChatOllama({ + baseUrl: params.url, // http://yourIP:11434 + model: params.model, // "llama2:7b-chat", codellama:13b-fast-instruct, elyza:13b-fast-instruct ... + temperature: params.temperature, + // maxTokens: params.maxTokens, // Not implemented yet on Langchain + }); +}; + +const createReplicateInstance = (params: Params): Replicate => { + if (isModel(params.model)) { // Model型に一致 + return new Replicate(); + } else { + throw new Error( + `Invalid reference to model version: "${params.model}". Expected format: owner/name or owner/name:version `, + ); + } +}; diff --git a/lib/params.ts b/lib/params.ts index b221ef7..20e6e21 100644 --- a/lib/params.ts +++ b/lib/params.ts @@ -1,21 +1,5 @@ import { parse } from "https://deno.land/std/flags/mod.ts"; - -// Platformオプション -// llamaモデルは共通のオープンモデルなので、 -// どこで実行するかをオプションで決める必要がある -export const platformList = [ - "ollama", - "groq", - "togetherai", - "replicate", -] as const; -type Platform = (typeof platformList)[number]; - -/** Platform型であることを保証する */ -function isPlatform(value: unknown): value is Platform { - return typeof value === "string" && - platformList.includes(value as Platform); -} +import { isPlatform, Platform, platformList } from "./llm.ts"; export type Params = { version: boolean; @@ -68,7 +52,11 @@ export function parseArgs(): Params { // platform の値を検証 const platform = args.p || args.platform; if (platform !== undefined && !isPlatform(platform)) { - throw new Error(`Platform must be one of: ${platformList.join(", ")}`); + throw new Error( + `You must choose one of these Platforms : --platform=${ + platformList.join(", ") + }`, + ); } return { diff --git a/test/llm_test.ts b/test/llm_test.ts index 0d576f8..dfe73d0 100644 --- a/test/llm_test.ts +++ b/test/llm_test.ts @@ -76,6 +76,7 @@ Deno.test("Should create a ChatOllama instance for an Ollama model", () => { debug: false, model: "llama:7b-chat", url: "http://yourIP:11434", + platform: "ollama", temperature: 0.7, maxTokens: 2048, }; @@ -94,6 +95,7 @@ Deno.test("Should create a Replicate instance for an Replicate model", () => { debug: false, model: "meta/llama2:7b-chat", url: undefined, + platform: "replicate", temperature: 0.7, maxTokens: 2048, }; @@ -114,7 +116,11 @@ Deno.test("Should throw an error for an unknown model", () => { temperature: 0.7, maxTokens: 2048, }; - assertThrows(() => new LLM(params), Error, 'model not found "unknown-model"'); + assertThrows( + () => new LLM(params), + Error, + "You must choose one of these Platforms : --platform=ollama, groq, togetherai, replicate", + ); }); Deno.test("Replicate prompt generator", () => { diff --git a/test/params_test.ts b/test/params_test.ts index df8e5e3..3805313 100644 --- a/test/params_test.ts +++ b/test/params_test.ts @@ -14,6 +14,7 @@ Deno.test("parseArgs", () => { temperature: 1.0, maxTokens: 1000, url: undefined, + platform: undefined, systemPrompt: undefined, content: undefined, }, @@ -29,6 +30,7 @@ Deno.test("parseArgs", () => { temperature: 1.0, maxTokens: 1000, url: undefined, + platform: undefined, systemPrompt: undefined, content: undefined, }, @@ -44,6 +46,8 @@ Deno.test("parseArgs", () => { "500", "-u", "https://example.com", + "-p", + "groq", "-s", "You are a helpful AI assistant.", "Hello, world!", @@ -57,6 +61,7 @@ Deno.test("parseArgs", () => { temperature: 0.5, maxTokens: 500, url: "https://example.com", + platform: "groq", systemPrompt: "You are a helpful AI assistant.", content: "Hello, world!", }, From add4210e42b06487699f4d332a37934c7e2dbf50 Mon Sep 17 00:00:00 2001 From: u1and0 Date: Thu, 26 Dec 2024 00:55:46 +0900 Subject: [PATCH 16/32] [chore] deno v2 --- .github/workflows/deno.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deno.yml b/.github/workflows/deno.yml index 6051e41..874d01a 100644 --- a/.github/workflows/deno.yml +++ b/.github/workflows/deno.yml @@ -38,10 +38,10 @@ jobs: uses: actions/checkout@v4 - name: Setup Deno - uses: denoland/setup-deno@v1 + uses: denoland/setup-deno@v2 # uses: denoland/setup-deno@61fe2df320078202e33d7d5ad347e7dcfa0e8f31 # v1.1.2 with: - deno-version: v1.x + deno-version: v2.x.x # Uncomment this step to verify the use of 'deno fmt' on each commit. - name: Verify formatting From ed80c62630a62789c39b231006c586f8e2e86021 Mon Sep 17 00:00:00 2001 From: u1and0 Date: Thu, 26 Dec 2024 01:02:51 +0900 Subject: [PATCH 17/32] [chore] no check --- .github/workflows/deno.yml | 6 +++--- lib/llm.ts | 9 ++++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/deno.yml b/.github/workflows/deno.yml index 874d01a..76a136c 100644 --- a/.github/workflows/deno.yml +++ b/.github/workflows/deno.yml @@ -50,11 +50,11 @@ jobs: - name: Run linter run: deno lint index.ts lib/*.ts test/*.ts - - name: Run checker - run: deno check index.ts lib/*.ts test/*.ts + # - name: Run checker + # run: deno check index.ts lib/*.ts test/*.ts - name: Run tests - run: deno test --allow-env test/*.ts + run: deno test --allow-env --no-check test/*.ts builder: name: compile & publish artifact diff --git a/lib/llm.ts b/lib/llm.ts index d29da28..fa4492e 100644 --- a/lib/llm.ts +++ b/lib/llm.ts @@ -40,6 +40,7 @@ export class LLM { | ChatOllama | ChatGoogleGenerativeAI | ChatGroq + | ChatTogetherAI | Replicate | undefined; @@ -294,7 +295,7 @@ const createOpenAIInstance = (params: Params): ChatOpenAI => { }); }; -const createOpenAIOModelINstance = (params: Params) => { +const createOpenAIOModelINstance = (params: Params): ChatOpenAI => { return new ChatOpenAI({ modelName: params.model, temperature: params.temperature, @@ -302,7 +303,7 @@ const createOpenAIOModelINstance = (params: Params) => { }); }; -const createAnthropicInstance = (params: Params) => { +const createAnthropicInstance = (params: Params): ChatAnthropic => { return new ChatAnthropic({ modelName: params.model, temperature: params.temperature, @@ -310,7 +311,9 @@ const createAnthropicInstance = (params: Params) => { }); }; -const createGoogleGenerativeAIInstance = (params: Params) => { +const createGoogleGenerativeAIInstance = ( + params: Params, +): ChatGoogleGenerativeAI => { return new ChatGoogleGenerativeAI({ model: params.model, temperature: params.temperature, From 6691a174ec62260b8b5ad30c314ee17cf7142b2c Mon Sep 17 00:00:00 2001 From: u1and0 Date: Thu, 26 Dec 2024 01:04:24 +0900 Subject: [PATCH 18/32] [chore] deno v2 --- .github/workflows/deno.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deno.yml b/.github/workflows/deno.yml index 76a136c..4ec417b 100644 --- a/.github/workflows/deno.yml +++ b/.github/workflows/deno.yml @@ -75,10 +75,10 @@ jobs: uses: actions/checkout@v4 - name: Setup Deno - uses: denoland/setup-deno@v1 + uses: denoland/setup-deno@v2 # uses: denoland/setup-deno@61fe2df320078202e33d7d5ad347e7dcfa0e8f31 # v1.1.2 with: - deno-version: v1.x + deno-version: v2.x.x - name: Build binary run: deno compile --allow-net --allow-env --output gpt index.ts From e46184213dcd3ea17e75e367cd3fd460f69a5766 Mon Sep 17 00:00:00 2001 From: u1and0 Date: Thu, 26 Dec 2024 02:06:59 +0900 Subject: [PATCH 19/32] [feat] remove platform option instead `--model platform/model` option --- README.md | 1 - lib/help.ts | 1 - lib/llm.ts | 48 ++++++++++++++++++++--------- test/llm_test.ts | 79 +++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 108 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index a663119..1ba0542 100644 --- a/README.md +++ b/README.md @@ -142,7 +142,6 @@ $ gpt -m gpt-4o-mini -x 1000 -t 1.0 [OPTIONS] PROMPT | -x | --max\_tokens | number | Number of AI answer tokens (default 1000) | | -t | --temperature | number | Higher number means more creative answers, lower number means more exact answers (default 1.0) | | -u | --url | string | URL and port number for ollama server | -| -p | --platform | string | Platform choose from ollama, groq, replicate | | -s | --system-prompt | string | The first instruction given to guide the AI model's response. | | -n | --no-chat | boolean | No chat mode. Just one time question and answer. | diff --git a/lib/help.ts b/lib/help.ts index 9dc4cdd..4ca2ff7 100644 --- a/lib/help.ts +++ b/lib/help.ts @@ -25,7 +25,6 @@ export const helpMessage = -x, --max-tokens: number Number of AI answer tokens (default 1000) -t, --temperature: number Higher number means more creative answers, lower number means more exact answers (default 1.0) -u, --url: string URL and port number for ollama server - -p, --platform: string Platform choose from ${platformList.join(", ")} -s, --system-prompt: string The first instruction given to guide the AI model's response -n, --no-chat: boolean No chat mode. Just one time question and answer. PROMPT: diff --git a/lib/llm.ts b/lib/llm.ts index fa4492e..d9d2f06 100644 --- a/lib/llm.ts +++ b/lib/llm.ts @@ -240,7 +240,7 @@ function llmConstructor(params: Params): | Replicate { const modelMap: ModelMap = { "^gpt": createOpenAIInstance, - "^o[0-9]": createOpenAIOModelINstance, + "^o[0-9]": createOpenAIOModelInstance, "^claude": createAnthropicInstance, "^gemini": createGoogleGenerativeAIInstance, } as const; @@ -268,20 +268,20 @@ function llmConstructor(params: Params): // // llamaなどのオープンモデルはモデル名ではなく、 // platform名で判定する - // + + // platformが特定できないときは空文字が返る + const { platform, model } = parsePlatform(params.model); // platformがオプションに指定されていなければエラー - if (params.platform === undefined) { + if (!isPlatform(platform)) { throw new Error( - `You must choose one of these Platforms : --platform=${ - platformList.join(", ") - }`, + `unknown platform "${platform}", choose from ${platformList.join(", ")}`, ); } // platformMap からオプションに指定したものがなければエラー - const createInstanceFromPlatform = platformMap[params.platform]; + const createInstanceFromPlatform = platformMap[platform]; if (createInstanceFromPlatform === undefined) { - throw new Error(`unknown model ${params.model}`); + throw new Error(`unknown model ${model}`); } return createInstanceFromPlatform(params); @@ -295,7 +295,7 @@ const createOpenAIInstance = (params: Params): ChatOpenAI => { }); }; -const createOpenAIOModelINstance = (params: Params): ChatOpenAI => { +const createOpenAIOModelInstance = (params: Params): ChatOpenAI => { return new ChatOpenAI({ modelName: params.model, temperature: params.temperature, @@ -322,16 +322,18 @@ const createGoogleGenerativeAIInstance = ( }; const createGroqInstance = (params: Params): ChatGroq => { + const { platform: _platform, model } = parsePlatform(params.model); return new ChatGroq({ - model: params.model, + model: model, temperature: params.temperature, maxTokens: params.maxTokens, }); }; const createTogetherAIInstance = (params: Params): ChatTogetherAI => { + const { platform: _platform, model } = parsePlatform(params.model); return new ChatTogetherAI({ - model: params.model, + model: model, temperature: params.temperature, maxTokens: params.maxTokens, }); @@ -344,20 +346,38 @@ const createOllamaInstance = (params: Params): ChatOllama => { "ollama needs URL parameter with `--url http://your.host:11434`", ); } + const { platform: _platform, model } = parsePlatform(params.model); return new ChatOllama({ baseUrl: params.url, // http://yourIP:11434 - model: params.model, // "llama2:7b-chat", codellama:13b-fast-instruct, elyza:13b-fast-instruct ... + model: model, // "llama2:7b-chat", codellama:13b-fast-instruct, elyza:13b-fast-instruct ... temperature: params.temperature, // maxTokens: params.maxTokens, // Not implemented yet on Langchain }); }; const createReplicateInstance = (params: Params): Replicate => { - if (isModel(params.model)) { // Model型に一致 + const { platform: _platform, model } = parsePlatform(params.model); + if (isModel(model)) { // Model型に一致 return new Replicate(); } else { throw new Error( - `Invalid reference to model version: "${params.model}". Expected format: owner/name or owner/name:version `, + `Invalid reference to model version: "${model}". Expected format: owner/name or owner/name:version `, ); } }; + +/** 1つ目の"/"で引数を分割して、 + * 1つ目をplatformとして、 + * 2つめ移行をmodelとして返す + */ +export function parsePlatform( + model: string, +): { platform: string; model: string } { + const parts = model.split("/"); + if (parts.length < 2) { + return { platform: "", model: model }; + } + const platform = parts[0]; + const modelName = parts.slice(1).join("/"); + return { platform, model: modelName }; +} diff --git a/test/llm_test.ts b/test/llm_test.ts index dfe73d0..d287ddb 100644 --- a/test/llm_test.ts +++ b/test/llm_test.ts @@ -5,6 +5,8 @@ import { ChatOpenAI } from "npm:@langchain/openai"; import { ChatAnthropic } from "npm:@langchain/anthropic"; import { ChatGoogleGenerativeAI } from "npm:@langchain/google-genai"; import { ChatOllama } from "npm:@langchain/community/chat_models/ollama"; +import { ChatGroq } from "npm:@langchain/groq"; +import { ChatTogetherAI } from "npm:@langchain/community/chat_models/togetherai"; import Replicate from "npm:replicate"; import { AIMessage, @@ -12,7 +14,12 @@ import { SystemMessage, } from "npm:@langchain/core/messages"; -import { generatePrompt, LLM } from "../lib/llm.ts"; +import { + generatePrompt, + LLM, + parsePlatform, + platformList, +} from "../lib/llm.ts"; Deno.test("Should create a ChatOpenAI instance for a GPT model", () => { Deno.env.set("OPENAI_API_KEY", "sk-11111"); @@ -74,7 +81,7 @@ Deno.test("Should create a ChatOllama instance for an Ollama model", () => { help: false, noChat: false, debug: false, - model: "llama:7b-chat", + model: "ollama/llama:7b-chat", url: "http://yourIP:11434", platform: "ollama", temperature: 0.7, @@ -93,9 +100,8 @@ Deno.test("Should create a Replicate instance for an Replicate model", () => { help: false, noChat: false, debug: false, - model: "meta/llama2:7b-chat", + model: "replicate/meta/llama2:7b-chat", url: undefined, - platform: "replicate", temperature: 0.7, maxTokens: 2048, }; @@ -106,6 +112,45 @@ Deno.test("Should create a Replicate instance for an Replicate model", () => { ); }); +Deno.test("Should create a Groq instance for an Groq model", () => { + const params = { + version: false, + help: false, + noChat: false, + debug: false, + model: "groq/llama-3.3-70b-versatile", + url: undefined, + temperature: 0.7, + maxTokens: 2048, + }; + const llm = new LLM(params); + assert( + llm.transrator instanceof ChatGroq, + `Expected LLM instance to be ChatGroq, but got ${llm.constructor.name}`, + ); + assertEquals(llm.transrator.model, "llama-3.3-70b-versatile"); + assertEquals(llm.transrator.temperature, 0.7); +}); + +Deno.test("Should create a TogetherAI instance for an TogetherAI model", () => { + const params = { + version: false, + help: false, + noChat: false, + debug: false, + model: "togetherai/google/gemma-2-27b-it", + url: undefined, + temperature: 0.7, + maxTokens: 2048, + }; + const llm = new LLM(params); + assert( + llm.transrator instanceof ChatTogetherAI, + `Expected LLM instance to be ChatTogetherAI, but got ${llm.constructor.name}`, + ); + assertEquals(llm.transrator.model, "google/gemma-2-27b-it"); +}); + Deno.test("Should throw an error for an unknown model", () => { const params = { version: false, @@ -119,7 +164,7 @@ Deno.test("Should throw an error for an unknown model", () => { assertThrows( () => new LLM(params), Error, - "You must choose one of these Platforms : --platform=ollama, groq, togetherai, replicate", + `unknown platform "", choose from ${platformList.join(", ")}`, ); }); @@ -161,3 +206,27 @@ hello, how can I help you? I have no name, just an AI`, ); }); + +Deno.test("parsePlatform - valid model string", () => { + const { platform, model } = parsePlatform("replicate/meta/llama3.3-70b"); + assertEquals(platform, "replicate"); + assertEquals(model, "meta/llama3.3-70b"); +}); + +Deno.test("parsePlatform - model string with only one part", () => { + const { platform, model } = parsePlatform("modelonly"); + assertEquals(platform, ""); + assertEquals(model, "modelonly"); +}); + +Deno.test("parsePlatform - model string with multiple slashes", () => { + const { platform, model } = parsePlatform("a/b/c/d"); + assertEquals(platform, "a"); + assertEquals(model, "b/c/d"); +}); + +Deno.test("parsePlatform - model string starts with slash", () => { + const { platform, model } = parsePlatform("/a/b/c"); + assertEquals(platform, ""); + assertEquals(model, "a/b/c"); +}); From c6457e6dec219ee9b7f271270498026907310edd Mon Sep 17 00:00:00 2001 From: u1and0 Date: Thu, 26 Dec 2024 02:08:59 +0900 Subject: [PATCH 20/32] [feat] no platform --- lib/params.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/lib/params.ts b/lib/params.ts index 20e6e21..288ea1b 100644 --- a/lib/params.ts +++ b/lib/params.ts @@ -10,7 +10,6 @@ export type Params = { temperature: number; maxTokens: number; url?: string; - platform?: Platform; systemPrompt?: string; content?: string; }; @@ -40,8 +39,6 @@ export function parseArgs(): Params { "temperature", "x", "max-tokens", - "p", - "platform", ], default: { temperature: 1.0, @@ -49,16 +46,6 @@ export function parseArgs(): Params { }, }); - // platform の値を検証 - const platform = args.p || args.platform; - if (platform !== undefined && !isPlatform(platform)) { - throw new Error( - `You must choose one of these Platforms : --platform=${ - platformList.join(", ") - }`, - ); - } - return { // boolean option version: args.v || args.version || false, @@ -70,7 +57,6 @@ export function parseArgs(): Params { maxTokens: parseInt(String(args.x || args["max-tokens"])), temperature: parseFloat(String(args.t || args.temperature)), url: args.u || args.url || undefined, - platform: platform as Platform | undefined, systemPrompt: args.s || args["system-prompt"] || undefined, content: args._.length > 0 ? args._.join(" ") : undefined, // 残りの引数をすべてスペースで結合 }; From 8240ab07b10dbacb98c5856f1d91bf8a131553ca Mon Sep 17 00:00:00 2001 From: u1and0 Date: Sat, 28 Dec 2024 23:42:34 +0900 Subject: [PATCH 21/32] [docs] comment --- index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/index.ts b/index.ts index 7f117f7..4da51e6 100644 --- a/index.ts +++ b/index.ts @@ -77,6 +77,7 @@ const llmAsk = async (params: Params) => { humanMessage.content.toString(), ); // モデル名指定以外のプロンプトがなければ前のプロンプトを引き継ぐ。 + // 前のプロンプトもなければ空のHumanMessageを渡す humanMessage = message || messages.at(-2) || new HumanMessage(""); // @コマンドで指定したモデルのパースに成功したら From def421b329ade6f7a05e0f73ed476b7b568fdcdb Mon Sep 17 00:00:00 2001 From: u1and0 Date: Sun, 29 Dec 2024 11:39:10 +0900 Subject: [PATCH 22/32] [test] remove platform --- test/params_test.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/params_test.ts b/test/params_test.ts index 3805313..ddf2a45 100644 --- a/test/params_test.ts +++ b/test/params_test.ts @@ -14,7 +14,6 @@ Deno.test("parseArgs", () => { temperature: 1.0, maxTokens: 1000, url: undefined, - platform: undefined, systemPrompt: undefined, content: undefined, }, @@ -30,7 +29,6 @@ Deno.test("parseArgs", () => { temperature: 1.0, maxTokens: 1000, url: undefined, - platform: undefined, systemPrompt: undefined, content: undefined, }, @@ -61,7 +59,6 @@ Deno.test("parseArgs", () => { temperature: 0.5, maxTokens: 500, url: "https://example.com", - platform: "groq", systemPrompt: "You are a helpful AI assistant.", content: "Hello, world!", }, From aa6b4076a94364067ae9a326c16c3ca557f8cced Mon Sep 17 00:00:00 2001 From: u1and0 Date: Sun, 29 Dec 2024 11:42:27 +0900 Subject: [PATCH 23/32] [fix] remove platform --- lib/help.ts | 2 -- lib/params.ts | 1 - 2 files changed, 3 deletions(-) diff --git a/lib/help.ts b/lib/help.ts index 4ca2ff7..6e39401 100644 --- a/lib/help.ts +++ b/lib/help.ts @@ -1,5 +1,3 @@ -import { platformList } from "./llm.ts"; - export const commandMessage = ` Ctrl+D to confirm input. diff --git a/lib/params.ts b/lib/params.ts index 288ea1b..01b75c7 100644 --- a/lib/params.ts +++ b/lib/params.ts @@ -1,5 +1,4 @@ import { parse } from "https://deno.land/std/flags/mod.ts"; -import { isPlatform, Platform, platformList } from "./llm.ts"; export type Params = { version: boolean; From 10d95b06e2f26dc72e708ea9bdd9931e88b8476f Mon Sep 17 00:00:00 2001 From: u1and0 Date: Sun, 29 Dec 2024 12:03:42 +0900 Subject: [PATCH 24/32] [test] add api keys --- test/llm_test.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/llm_test.ts b/test/llm_test.ts index d287ddb..acda447 100644 --- a/test/llm_test.ts +++ b/test/llm_test.ts @@ -113,6 +113,7 @@ Deno.test("Should create a Replicate instance for an Replicate model", () => { }); Deno.test("Should create a Groq instance for an Groq model", () => { + Deno.env.set("GROQ_API_KEY", "sk-11111"); const params = { version: false, help: false, @@ -133,6 +134,7 @@ Deno.test("Should create a Groq instance for an Groq model", () => { }); Deno.test("Should create a TogetherAI instance for an TogetherAI model", () => { + Deno.env.set("TOGETHER_AI_API_KEY", "sk-11111"); const params = { version: false, help: false, From ec43f377a229ae0806bf06cbbacaee8ceb94193d Mon Sep 17 00:00:00 2001 From: u1and0 Date: Mon, 30 Dec 2024 00:23:12 +0900 Subject: [PATCH 25/32] [test] remove platform --- index.ts | 2 +- lib/help.ts | 2 -- test/params_test.ts | 5 ----- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/index.ts b/index.ts index 4da51e6..c693ca2 100644 --- a/index.ts +++ b/index.ts @@ -34,7 +34,7 @@ import { modelStack, } from "./lib/command.ts"; -const VERSION = "v1.0.0"; +const VERSION = "v0.8.0"; /** 灰色のテキストで表示 */ function consoleInfoWithGrayText(s: string): void { diff --git a/lib/help.ts b/lib/help.ts index 4ca2ff7..6e39401 100644 --- a/lib/help.ts +++ b/lib/help.ts @@ -1,5 +1,3 @@ -import { platformList } from "./llm.ts"; - export const commandMessage = ` Ctrl+D to confirm input. diff --git a/test/params_test.ts b/test/params_test.ts index 3805313..df8e5e3 100644 --- a/test/params_test.ts +++ b/test/params_test.ts @@ -14,7 +14,6 @@ Deno.test("parseArgs", () => { temperature: 1.0, maxTokens: 1000, url: undefined, - platform: undefined, systemPrompt: undefined, content: undefined, }, @@ -30,7 +29,6 @@ Deno.test("parseArgs", () => { temperature: 1.0, maxTokens: 1000, url: undefined, - platform: undefined, systemPrompt: undefined, content: undefined, }, @@ -46,8 +44,6 @@ Deno.test("parseArgs", () => { "500", "-u", "https://example.com", - "-p", - "groq", "-s", "You are a helpful AI assistant.", "Hello, world!", @@ -61,7 +57,6 @@ Deno.test("parseArgs", () => { temperature: 0.5, maxTokens: 500, url: "https://example.com", - platform: "groq", systemPrompt: "You are a helpful AI assistant.", content: "Hello, world!", }, From 6e7715528968454eeef3fe54ce7c4b75ad3899d6 Mon Sep 17 00:00:00 2001 From: u1and0 Date: Mon, 30 Dec 2024 01:12:29 +0900 Subject: [PATCH 26/32] [refactor] type --- README.md | 44 ++++++++++++++++++++++---------------------- lib/llm.ts | 52 ++++++++++++++++------------------------------------ 2 files changed, 38 insertions(+), 58 deletions(-) diff --git a/README.md b/README.md index 1ba0542..568494a 100644 --- a/README.md +++ b/README.md @@ -159,34 +159,34 @@ A Questions for Model - o1-mini... - [Anthropic](https://docs.anthropic.com/claude/docs/models-overview) - claude-3-opus-20240229 - - claude-3-haiku-20240307 - - claude-instant-1.2... + - claude-3-5-sonnet-latest + - claude-3-5-haiku-latest - [Gemini](https://ai.google.dev/gemini-api/docs/models/gemini) - gemini-1.5-pro-latest - - gemini-pro + - gemini-2.0-flash-exp... - [Replicate](https://replicate.com/models) - - meta/meta-llama-3-70b-instruct - - meta/llama-2-7b-chat - - mistralai/mistral-7b-instruct-v0.2 - - mistralai/mixtral-8x7b-instruct-v0.1 - - snowflake/snowflake-arctic-instruct - - replicate/flan-t5-xl... + - replicate/meta/meta-llama-3-70b-instruct + - replicate/meta/llama-2-7b-chat + - replicate/mistralai/mistral-7b-instruct-v0.2 + - replicate/mistralai/mixtral-8x7b-instruct-v0.1 + - replicate/snowflake/snowflake-arctic-instruct + - replicate/replicate/flan-t5-xl... - [Ollama](https://ollama.com/library) ** Using before "$ ollama serve" locally ** - - phi3 - - llama3:70b - - mixtral:8x7b-text-v0.1-q5\_K\_M... + - ollama/phi3 + - ollama/llama3:70b + - ollama/mixtral:8x7b-text-v0.1-q5\_K\_M... - [Groq](https://console.groq.com/docs/models) ** Using before "$ ollama serve" locally ** - - llama3-groq-70b-8192-tool-use-preview - - llama-3.3-70b-specdec - - llama3.1-70b-specdec - - llama-3.2-1b-preview - - llama-3.2-3b-preview... + - groq/llama3-groq-70b-8192-tool-use-preview + - groq/llama-3.3-70b-specdec + - groq/llama3.1-70b-specdec + - groq/llama-3.2-1b-preview + - groq/llama-3.2-3b-preview... - [TogetherAI](https://api.together.ai/models) ** Using before "$ ollama serve" locally ** - - meta-llama/Llama-3.3-70B-Instruct-Turbo - - Qwen/QwQ-32B-Preview - - meta-llama/Llama-3.1-405B-Instruct-Turbo - - google/gemma-2-27b-it - - mistralai/Mistral-7B-Instruct-v0.3... + - togetherai/meta-llama/Llama-3.3-70B-Instruct-Turbo + - togetherai/Qwen/QwQ-32B-Preview + - togetherai/meta-llama/Llama-3.1-405B-Instruct-Turbo + - togetherai/google/gemma-2-27b-it + - togetherai/mistralai/Mistral-7B-Instruct-v0.3... ## / command Help (/commands): diff --git a/lib/llm.ts b/lib/llm.ts index d9d2f06..0e188f2 100644 --- a/lib/llm.ts +++ b/lib/llm.ts @@ -22,27 +22,22 @@ export type Message = AIMessage | HumanMessage | SystemMessage | never; //{ role /** replicateで使うモデルは以下の形式 * owner/name or owner/name:version */ -type Model = `${string}/${string}`; +type ReplicateModel = `${string}/${string}`; -/** Model型であることを保証する */ -const isModel = (value: unknown): value is Model => { +/** ReplicateModel型であることを保証する */ +const isReplicateModel = (value: unknown): value is ReplicateModel => { return typeof value === "string" && value.includes("/") && value.split("/").length === 2; }; +type OpenModel = ChatGroq | ChatTogetherAI | ChatOllama | Replicate; +type CloseModel = ChatOpenAI | ChatAnthropic | ChatGoogleGenerativeAI; + /** Chatインスタンスを作成する * @param: Params - LLMのパラメータ、モデル */ export class LLM { - public readonly transrator: - | ChatOpenAI - | ChatAnthropic - | ChatOllama - | ChatGoogleGenerativeAI - | ChatGroq - | ChatTogetherAI - | Replicate - | undefined; + public readonly transrator?: OpenModel | CloseModel; constructor(private readonly params: Params) { this.transrator = llmConstructor(params); @@ -95,7 +90,7 @@ export class LLM { } else { const input = this.generateInput(messages); return (this.transrator as Replicate).stream( - this.params.model as Model, + this.params.model as ReplicateModel, { input }, ) as AsyncGenerator; } @@ -193,11 +188,7 @@ async function* streamEncoder( } } -type ModelMap = { - [key: string]: ( - params: Params, - ) => ChatOpenAI | ChatAnthropic | ChatGoogleGenerativeAI; -}; +type ModelMap = { [key: string]: (params: Params) => CloseModel }; // Platformオプション // llamaモデルは共通のオープンモデルなので、 @@ -218,11 +209,7 @@ export function isPlatform(value: unknown): value is Platform { } /** Platformごとに返すモデルのインスタンスを返す関数 */ -type PlatformMap = { - [key in Platform]: ( - params: Params, - ) => ChatGroq | ChatTogetherAI | ChatOllama | Replicate; -}; +type PlatformMap = { [key in Platform]: (params: Params) => OpenModel }; /** LLM クラスのtransratorプロパティをparamsから判定し、 * LLM インスタンスを生成して返す。 @@ -230,14 +217,7 @@ type PlatformMap = { * @return : LLM model * @throws{Error} model not found "${params.model}" */ -function llmConstructor(params: Params): - | ChatOpenAI - | ChatAnthropic - | ChatGoogleGenerativeAI - | ChatGroq - | ChatTogetherAI - | ChatOllama - | Replicate { +function llmConstructor(params: Params): OpenModel | CloseModel { const modelMap: ModelMap = { "^gpt": createOpenAIInstance, "^o[0-9]": createOpenAIOModelInstance, @@ -322,7 +302,7 @@ const createGoogleGenerativeAIInstance = ( }; const createGroqInstance = (params: Params): ChatGroq => { - const { platform: _platform, model } = parsePlatform(params.model); + const { platform: _, model } = parsePlatform(params.model); return new ChatGroq({ model: model, temperature: params.temperature, @@ -331,7 +311,7 @@ const createGroqInstance = (params: Params): ChatGroq => { }; const createTogetherAIInstance = (params: Params): ChatTogetherAI => { - const { platform: _platform, model } = parsePlatform(params.model); + const { platform: _, model } = parsePlatform(params.model); return new ChatTogetherAI({ model: model, temperature: params.temperature, @@ -346,7 +326,7 @@ const createOllamaInstance = (params: Params): ChatOllama => { "ollama needs URL parameter with `--url http://your.host:11434`", ); } - const { platform: _platform, model } = parsePlatform(params.model); + const { platform: _, model } = parsePlatform(params.model); return new ChatOllama({ baseUrl: params.url, // http://yourIP:11434 model: model, // "llama2:7b-chat", codellama:13b-fast-instruct, elyza:13b-fast-instruct ... @@ -356,8 +336,8 @@ const createOllamaInstance = (params: Params): ChatOllama => { }; const createReplicateInstance = (params: Params): Replicate => { - const { platform: _platform, model } = parsePlatform(params.model); - if (isModel(model)) { // Model型に一致 + const { platform: _, model } = parsePlatform(params.model); + if (isReplicateModel(model)) { return new Replicate(); } else { throw new Error( From 2e87436031b9e4680080a91d08c5393ae6625126 Mon Sep 17 00:00:00 2001 From: u1and0 Date: Mon, 30 Dec 2024 01:16:21 +0900 Subject: [PATCH 27/32] [docs] version --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 568494a..fccb1c7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ - + @@ -10,7 +10,7 @@ Command-line interface that enables interactive chat with LLMs. # Quick start ``` -$ curl -LO https://github.com/u1and0/gpt-cli/releases/download/v0.7.0/gpt-cli-linux.zip +$ curl -LO https://github.com/u1and0/gpt-cli/releases/download/v0.8.0/gpt-cli-linux.zip $ unzip gpt-cli.zip $ chmod 755 gpt $ sudo ln -s ./gpt /usr/bin @@ -46,7 +46,7 @@ You have 3 options. The simplest way. ``` -$ curl -LO https://github.com/u1and0/gpt-cli/releases/download/v0.7.0/gpt-cli-linux.zip +$ curl -LO https://github.com/u1and0/gpt-cli/releases/download/v0.8.0/gpt-cli-linux.zip $ unzip gpt-cli-linux.zip $ chmod 755 gpt $ sudo ln -s ./gpt /usr/bin From a0fd9d8e62501fcebdd8e4f702fdcc28889fbd34 Mon Sep 17 00:00:00 2001 From: u1and0 Date: Tue, 31 Dec 2024 00:57:00 +0900 Subject: [PATCH 28/32] [docs] set Groq, Together AI api key --- README.md | 46 +++++++++++++++++++++++++++++++--------------- lib/help.ts | 4 ++-- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index fccb1c7..e552f13 100644 --- a/README.md +++ b/README.md @@ -104,7 +104,7 @@ export OPENAI_API_KEY='sk-*****' export ANTHROPIC_API_KEY='sk-ant-*****' ``` -### Goolgle API (for Gemini) +### Google API (for Gemini) [Get Google API key](https://aistudio.google.com/app/apikey), then set environment argument. @@ -112,6 +112,22 @@ export ANTHROPIC_API_KEY='sk-ant-*****' export GOOGLE_API_KEY='*****' ``` +### Groq API + +[Get Groq API key](https://console.groq.com/keys), then set environment argument. + +``` +export GROQ_API_KEY='*****' +``` + +### Together AI API + +[Get Together AI API key](https://api.together.xyz/settings/api-keys), then set environment argument. + +``` +export TOGETHER_AI_API_KEY='*****' +``` + ### Replicate API (for Open Models) [Get Replicate API token](https://replicate.com/account/api-tokens), then set environment argument. @@ -138,7 +154,7 @@ $ gpt -m gpt-4o-mini -x 1000 -t 1.0 [OPTIONS] PROMPT |--------------|-------------|------|----| | -v | --version | boolean | Show version | | -h | --help | boolean | Show this message | -| -m | --model | string | OpenAI, Anthropic, Google, Replicate, Ollama model (default gpt-4o-mini) | +| -m | --model | string | LLM model (default gpt-4o-mini) | | -x | --max\_tokens | number | Number of AI answer tokens (default 1000) | | -t | --temperature | number | Higher number means more creative answers, lower number means more exact answers (default 1.0) | | -u | --url | string | URL and port number for ollama server | @@ -164,6 +180,18 @@ A Questions for Model - [Gemini](https://ai.google.dev/gemini-api/docs/models/gemini) - gemini-1.5-pro-latest - gemini-2.0-flash-exp... +- [Groq](https://console.groq.com/docs/models) + - groq/llama3-groq-70b-8192-tool-use-preview + - groq/llama-3.3-70b-specdec + - groq/llama3.1-70b-specdec + - groq/llama-3.2-1b-preview + - groq/llama-3.2-3b-preview... +- [TogetherAI](https://api.together.ai/models) + - togetherai/meta-llama/Llama-3.3-70B-Instruct-Turbo + - togetherai/Qwen/QwQ-32B-Preview + - togetherai/meta-llama/Llama-3.1-405B-Instruct-Turbo + - togetherai/google/gemma-2-27b-it + - togetherai/mistralai/Mistral-7B-Instruct-v0.3... - [Replicate](https://replicate.com/models) - replicate/meta/meta-llama-3-70b-instruct - replicate/meta/llama-2-7b-chat @@ -175,18 +203,6 @@ A Questions for Model - ollama/phi3 - ollama/llama3:70b - ollama/mixtral:8x7b-text-v0.1-q5\_K\_M... -- [Groq](https://console.groq.com/docs/models) ** Using before "$ ollama serve" locally ** - - groq/llama3-groq-70b-8192-tool-use-preview - - groq/llama-3.3-70b-specdec - - groq/llama3.1-70b-specdec - - groq/llama-3.2-1b-preview - - groq/llama-3.2-3b-preview... -- [TogetherAI](https://api.together.ai/models) ** Using before "$ ollama serve" locally ** - - togetherai/meta-llama/Llama-3.3-70B-Instruct-Turbo - - togetherai/Qwen/QwQ-32B-Preview - - togetherai/meta-llama/Llama-3.1-405B-Instruct-Turbo - - togetherai/google/gemma-2-27b-it - - togetherai/mistralai/Mistral-7B-Instruct-v0.3... ## / command Help (/commands): @@ -198,7 +214,7 @@ Help (/commands): ## @ command Help (@commands): Change model while asking. -- @{ModelName} Change LLM model +- @ModelName Change LLM model - ex) @gemini-1.5-pro your question... ## Test diff --git a/lib/help.ts b/lib/help.ts index 6e39401..8ac3b26 100644 --- a/lib/help.ts +++ b/lib/help.ts @@ -6,7 +6,7 @@ Help: /clear Clear session context /modelStack Show model's history /bye,/exit,/quit Exit - @{ModelName} Change LLM model + @ModelName Change LLM model ex) @gemini-1.5-pro your question... `; @@ -19,7 +19,7 @@ export const helpMessage = Options: -v, --version: boolean Show version -h, --help: boolean Show this message - -m, --model: string OpenAI, Anthropic, Google, Replicate, Ollama model (default gpt-4o-mini) + -m, --model: string LLM model (default gpt-4o-mini) -x, --max-tokens: number Number of AI answer tokens (default 1000) -t, --temperature: number Higher number means more creative answers, lower number means more exact answers (default 1.0) -u, --url: string URL and port number for ollama server From be42e1e1923ea75f123a3c1ea0a83bac713755a4 Mon Sep 17 00:00:00 2001 From: u1and0 Date: Tue, 31 Dec 2024 01:05:06 +0900 Subject: [PATCH 29/32] [docs] quick start --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index e552f13..813112a 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,16 @@ Command-line interface that enables interactive chat with LLMs. # Quick start ``` +# Install binary $ curl -LO https://github.com/u1and0/gpt-cli/releases/download/v0.8.0/gpt-cli-linux.zip $ unzip gpt-cli.zip $ chmod 755 gpt $ sudo ln -s ./gpt /usr/bin + +# Setup API key +$ export OPENAI_API_KEY='sk-*****' + +# Run $ gpt Ctrl-D to confirm input, q or exit to end chat You: hi From 51b9456d22d17a37b93b4ba689ecc44852647e93 Mon Sep 17 00:00:00 2001 From: u1and0 Date: Tue, 31 Dec 2024 01:13:03 +0900 Subject: [PATCH 30/32] [docs] help message --- README.md | 11 ++++++----- lib/help.ts | 45 ++++++++++++++++++++++----------------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 813112a..0c12bbf 100644 --- a/README.md +++ b/README.md @@ -213,9 +213,10 @@ A Questions for Model ## / command Help (/commands): -- /?, /help Help for a command -- /clear Clear session context -- /bye Exit +- /?, /help Help for a command +- /clear Clear session context +- /modelStack Show model's history +- /bye,/exit,/quit Exit ## @ command Help (@commands): Change model while asking. @@ -252,12 +253,12 @@ hook_add = ''' " Create test code command! -nargs=0 -range GPTGenerateTest ,call gptcli#GPT('You are the best code tester. Please write test code that covers all cases to try the given code.', { "temperature": 0.5, "model": "claude-3-haiku-20240307" }) command! -nargs=0 -range GPTErrorBustor ,call gptcli#GPT('Your task is to analyze the provided code snippet, identify any bugs or errors present, and provide a corrected version of the code that resolves these issues. Explain the problems you found in the original code and how your fixes address them. The corrected code should be functional, efficient, and adhere to best practices in programming.', {"temperature": 0.5, "model": "claude-3-sonnet-20240229"}) - command! -nargs=0 -range GPTCodeOptimizer ,call gptcli#GPT("Your task is to analyze the provided code snippet and suggest improvements to optimize its performance. Identify areas where the code can be made more efficient, faster, or less resource-intensive. Provide specific suggestions for optimization, along with explanations of how these changes can enhance the code performance. The optimized code should maintain the same functionality as the original code while demonstrating improved efficiency.", { "model": "meta/meta-llama-3-70b-instruct" }) + command! -nargs=0 -range GPTCodeOptimizer ,call gptcli#GPT("Your task is to analyze the provided code snippet and suggest improvements to optimize its performance. Identify areas where the code can be made more efficient, faster, or less resource-intensive. Provide specific suggestions for optimization, along with explanations of how these changes can enhance the code performance. The optimized code should maintain the same functionality as the original code while demonstrating improved efficiency.", { "model": "replicate/meta/meta-llama-3-70b-instruct" }) " Any system prompt command! -nargs=? -range GPTComplete ,call gptcli#GPT(, { "model": "claude-3-haiku-20240307" }) " Chat with GPT - command! -nargs=? GPTChat call gptcli#GPTWindow(, { "model": "phi3:instruct", "url": "http://localhost:11434"}) + command! -nargs=? GPTChat call gptcli#GPTWindow(, { "model": "ollama/phi3:instruct", "url": "http://localhost:11434"}) ``` ![Peek 2024-04-01 03-35.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113494/f243f19b-ee47-9821-5899-7ed2acc17320.gif) diff --git a/lib/help.ts b/lib/help.ts index 8ac3b26..b7fcaa5 100644 --- a/lib/help.ts +++ b/lib/help.ts @@ -42,30 +42,29 @@ export const helpMessage = - claude-instant-1.2... - [Gemini](https://ai.google.dev/gemini-api/docs/models/gemini) - gemini-1.5-pro-latest - - gemini-1.5-flash - - gemini-pro... + - gemini-2.0-flash-exp... + - [Groq](https://console.groq.com/docs/models) + - groq/llama3-groq-70b-8192-tool-use-preview + - groq/llama-3.3-70b-specdec + - groq/llama3.1-70b-specdec + - groq/llama-3.2-1b-preview + - groq/llama-3.2-3b-preview + - [TogetherAI](https://api.together.ai/models) + - togetherai/meta-llama/Llama-3.3-70B-Instruct-Turbo + - togetherai/Qwen/QwQ-32B-Preview + - togetherai/meta-llama/Llama-3.1-405B-Instruct-Turbo + - togetherai/google/gemma-2-27b-it + - togetherai/mistralai/Mistral-7B-Instruct-v0.3... - [Replicate](https://replicate.com/models) - - meta/meta-llama-3-70b-instruct - - meta/llama-2-7b-chat - - mistralai/mistral-7b-instruct-v0.2 - - mistralai/mixtral-8x7b-instruct-v0.1 - - snowflake/snowflake-arctic-instruct - - replicate/flan-t5-xl... + - replicate/meta/meta-llama-3-70b-instruct + - replicate/meta/llama-2-7b-chat + - replicate/mistralai/mistral-7b-instruct-v0.2 + - replicate/mistralai/mixtral-8x7b-instruct-v0.1 + - replicate/snowflake/snowflake-arctic-instruct + - replicate/replicate/flan-t5-xl... - [Ollama](https://ollama.com/library) ** Using before "$ ollama serve" locally ** - - phi3 - - llama3:70b - - mixtral:8x7b-text-v0.1-q5_K_M... - - [Groq](https://console.groq.com/docs/models) ** Using before "$ ollama serve" locally ** - - llama3-groq-70b-8192-tool-use-preview - - llama-3.3-70b-specdec - - llama3.1-70b-specdec - - llama-3.2-1b-preview - - llama-3.2-3b-preview - - [TogetherAI](https://api.together.ai/models) ** Using before "$ ollama serve" locally ** - - meta-llama/Llama-3.3-70B-Instruct-Turbo - - Qwen/QwQ-32B-Preview - - meta-llama/Llama-3.1-405B-Instruct-Turbo - - google/gemma-2-27b-it - - mistralai/Mistral-7B-Instruct-v0.3... + - ollama/phi3 + - ollama/llama3:70b + - ollama/mixtral:8x7b-text-v0.1-q5_K_M... ${commandMessage} `; From cab98b6e3a17c5a440b66ed55a9d4d64b8d8a0fa Mon Sep 17 00:00:00 2001 From: u1and0 Date: Tue, 31 Dec 2024 02:37:51 +0900 Subject: [PATCH 31/32] [chore] arm64 build --- .github/workflows/deno.yml | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/.github/workflows/deno.yml b/.github/workflows/deno.yml index 4ec417b..0ae45d9 100644 --- a/.github/workflows/deno.yml +++ b/.github/workflows/deno.yml @@ -50,11 +50,11 @@ jobs: - name: Run linter run: deno lint index.ts lib/*.ts test/*.ts - # - name: Run checker - # run: deno check index.ts lib/*.ts test/*.ts + - name: Run checker + run: deno check index.ts lib/*.ts test/*.ts - name: Run tests - run: deno test --allow-env --no-check test/*.ts + run: deno test --allow-env test/*.ts builder: name: compile & publish artifact @@ -63,12 +63,22 @@ jobs: strategy: matrix: include: - - name: linux + - name: linux-x64 vm: ubuntu-latest - - name: macosx + target: x86_64-unknown-linux-gnu + - name: linux-arm64 + vm: ubuntu-latest + target: aarch64-unknown-linux-gnu + - name: macosx-x64 + vm: macos-latest + target: x86_64-apple-darwin + - name: macosx-arm64 vm: macos-latest - - name: windows + target: aarch64-apple-darwin + - name: windows-x64 vm: windows-latest + target: x86_64-pc-windows-msvc + steps: - name: Setup repo @@ -81,7 +91,7 @@ jobs: deno-version: v2.x.x - name: Build binary - run: deno compile --allow-net --allow-env --output gpt index.ts + run: deno compile --allow-net --allow-env --target ${{ matrix.target }} --output gpt index.ts - name: Binary upload (Unix, MacOS) # Artifact upload only occurs when tag matches @@ -92,7 +102,6 @@ jobs: path: gpt - name: Binary upload (Windows) - if: runner.os == 'Windows' && startsWith(github.ref, 'refs/tags/v') uses: actions/upload-artifact@v4 with: name: gpt-cli-${{ matrix.name }} From bf4e3048d29400164fe0d09f74c7d28321eb7bac Mon Sep 17 00:00:00 2001 From: u1and0 Date: Tue, 31 Dec 2024 02:48:23 +0900 Subject: [PATCH 32/32] [chore] fix binary upload --- .github/workflows/deno.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/deno.yml b/.github/workflows/deno.yml index 0ae45d9..68b3fbd 100644 --- a/.github/workflows/deno.yml +++ b/.github/workflows/deno.yml @@ -102,6 +102,7 @@ jobs: path: gpt - name: Binary upload (Windows) + if: runner.os == 'Windows' && startsWith(github.ref, 'refs/tags/v') uses: actions/upload-artifact@v4 with: name: gpt-cli-${{ matrix.name }}