mirror of
https://github.com/enricoros/big-AGI.git
synced 2026-05-10 21:50:14 -07:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6053636f66 | |||
| f2e2aee672 | |||
| 11cbb2bbf0 | |||
| 30bd19d6ce | |||
| d0b5c02062 | |||
| 771192e406 |
@@ -21,7 +21,7 @@ shows the current developments and future ideas.
|
||||
- Got a suggestion? [_Add your roadmap ideas_](https://github.com/enricoros/big-agi/issues/new?&template=roadmap-request.md)
|
||||
- Want to contribute? [_Pick up a task!_](https://github.com/users/enricoros/projects/4/views/4) - _easy_ to _pro_
|
||||
|
||||
### What's New in 1.7.1 · Dec 11, 2023 · Attachment Theory 🌟
|
||||
### What's New in 1.7.2 · Dec 12, 2023 · Attachment Theory 🌟
|
||||
|
||||
- **Attachments System Overhaul**: Drag, paste, link, snap, text, images, PDFs and more. [#251](https://github.com/enricoros/big-agi/issues/251)
|
||||
- **Desktop Webcam Capture**: Image capture now available as Labs feature. [#253](https://github.com/enricoros/big-agi/issues/253)
|
||||
@@ -32,6 +32,7 @@ shows the current developments and future ideas.
|
||||
- Latest Ollama and Oobabooga models
|
||||
- For developers: **Password Protection**: HTTP Basic Auth. [Learn How](https://github.com/enricoros/big-agi/blob/main/docs/deploy-authentication.md)
|
||||
- [1.7.1]: Improved Ollama chats. [#270](https://github.com/enricoros/big-agi/issues/270)
|
||||
- [1.7.2]: Updated OpenRouter models (incl. Mixtral 8x7B)
|
||||
|
||||
### What's New in 1.6.0 - Nov 28, 2023
|
||||
|
||||
|
||||
+2
-1
@@ -10,7 +10,7 @@ by release.
|
||||
- work in progress: [big-AGI open roadmap](https://github.com/users/enricoros/projects/4/views/2), [help here](https://github.com/users/enricoros/projects/4/views/4)
|
||||
- milestone: [1.8.0](https://github.com/enricoros/big-agi/milestone/8)
|
||||
|
||||
### What's New in 1.7.1 · Dec 11, 2023 · Attachment Theory 🌟
|
||||
### What's New in 1.7.2 · Dec 11, 2023 · Attachment Theory 🌟
|
||||
|
||||
- **Attachments System Overhaul**: Drag, paste, link, snap, text, images, PDFs and more. [#251](https://github.com/enricoros/big-agi/issues/251)
|
||||
- **Desktop Webcam Capture**: Image capture now available as Labs feature. [#253](https://github.com/enricoros/big-agi/issues/253)
|
||||
@@ -21,6 +21,7 @@ by release.
|
||||
- Latest Ollama and Oobabooga models
|
||||
- For developers: **Password Protection**: HTTP Basic Auth. [Learn How](https://github.com/enricoros/big-agi/blob/main/docs/deploy-authentication.md)
|
||||
- [1.7.1]: Improved Ollama chats. [#270](https://github.com/enricoros/big-agi/issues/270)
|
||||
- [1.7.2]: Updated OpenRouter models (incl. Mixtral 8x7B)
|
||||
|
||||
### What's New in 1.6.0 - Nov 28, 2023 · Surf's Up
|
||||
|
||||
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "big-agi",
|
||||
"version": "1.7.1",
|
||||
"version": "1.7.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "big-agi",
|
||||
"version": "1.7.1",
|
||||
"version": "1.7.2",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@dqbd/tiktoken": "^1.0.7",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "big-agi",
|
||||
"version": "1.7.1",
|
||||
"version": "1.7.2",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
import * as React from 'react';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
import { Box, Typography } from '@mui/joy';
|
||||
|
||||
import { useModelsStore } from '~/modules/llms/store-llms';
|
||||
|
||||
import { AppLayout } from '~/common/layout/AppLayout';
|
||||
import { InlineError } from '~/common/components/InlineError';
|
||||
import { apiQuery } from '~/common/util/trpc.client';
|
||||
import { navigateToIndex } from '~/common/app.routes';
|
||||
import { openLayoutModelsSetup } from '~/common/layout/store-applayout';
|
||||
|
||||
|
||||
function CallbackOpenRouterPage(props: { openRouterCode: string | undefined }) {
|
||||
|
||||
// external state
|
||||
const { data, isError, error, isLoading } = apiQuery.backend.exchangeOpenRouterKey.useQuery({ code: props.openRouterCode || '' }, {
|
||||
enabled: !!props.openRouterCode,
|
||||
refetchOnWindowFocus: false,
|
||||
staleTime: Infinity,
|
||||
});
|
||||
|
||||
// derived state
|
||||
const isErrorInput = !props.openRouterCode;
|
||||
const openRouterKey = data?.key ?? undefined;
|
||||
const isSuccess = !!openRouterKey;
|
||||
|
||||
|
||||
// Success: save the key and redirect to the chat app
|
||||
React.useEffect(() => {
|
||||
if (!isSuccess)
|
||||
return;
|
||||
|
||||
// 1. Save the key as the client key
|
||||
useModelsStore.getState().setOpenRoutersKey(openRouterKey);
|
||||
|
||||
// 2. Navigate to the chat app
|
||||
navigateToIndex(true).then(() => openLayoutModelsSetup());
|
||||
|
||||
}, [isSuccess, openRouterKey]);
|
||||
|
||||
return (
|
||||
<Box sx={{
|
||||
flexGrow: 1,
|
||||
backgroundColor: 'background.level1',
|
||||
overflowY: 'auto',
|
||||
display: 'flex', justifyContent: 'center',
|
||||
p: { xs: 3, md: 6 },
|
||||
}}>
|
||||
|
||||
<Box sx={{
|
||||
// my: 'auto',
|
||||
display: 'flex', flexDirection: 'column', alignItems: 'center',
|
||||
gap: 4,
|
||||
}}>
|
||||
|
||||
<Typography level='title-lg'>
|
||||
Welcome Back
|
||||
</Typography>
|
||||
|
||||
{isLoading && <Typography level='body-sm'>Loading...</Typography>}
|
||||
|
||||
{isErrorInput && <InlineError error='There was an issue retrieving the code from OpenRouter.' />}
|
||||
|
||||
{isError && <InlineError error={error} />}
|
||||
|
||||
{data && (
|
||||
<Typography level='body-md'>
|
||||
Success! You can now close this window.
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
</Box>
|
||||
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This page will be invoked by OpenRouter as a Callback
|
||||
*
|
||||
* Docs: https://openrouter.ai/docs#oauth
|
||||
* Example URL: https://localhost:3000/link/callback_openrouter?code=SomeCode
|
||||
*/
|
||||
export default function Page() {
|
||||
|
||||
// get the 'code=...' from the URL
|
||||
const { query } = useRouter();
|
||||
const { code: openRouterCode } = query;
|
||||
|
||||
return (
|
||||
<AppLayout suspendAutoModelsSetup>
|
||||
<CallbackOpenRouterPage openRouterCode={openRouterCode as (string | undefined)} />
|
||||
</AppLayout>
|
||||
);
|
||||
}
|
||||
@@ -254,7 +254,7 @@ export async function attachmentPerformConversion(attachment: Readonly<Attachmen
|
||||
case 'rich-text-table':
|
||||
let mdTable: string;
|
||||
try {
|
||||
mdTable = htmlTableToMarkdown(input.altData!);
|
||||
mdTable = htmlTableToMarkdown(input.altData!, false);
|
||||
} catch (error) {
|
||||
// fallback to text/plain
|
||||
mdTable = inputDataToString(input.data);
|
||||
|
||||
@@ -67,7 +67,7 @@ export const NewsItems: NewsItem[] = [
|
||||
],
|
||||
},*/
|
||||
{
|
||||
versionCode: '1.7.1',
|
||||
versionCode: '1.7.2',
|
||||
versionName: 'Attachment Theory',
|
||||
versionDate: new Date('2023-12-11T06:00:00Z'), // new Date().toISOString()
|
||||
// versionDate: new Date('2023-12-10T12:00:00Z'), // 1.7.0
|
||||
@@ -81,6 +81,7 @@ export const NewsItems: NewsItem[] = [
|
||||
{ text: <>Optimized voice input and performance</> },
|
||||
{ text: <>Latest Ollama and Oobabooga models</> },
|
||||
{ text: <>1.7.1: Improved <B href={RIssues + '/270'}>Ollama chats</B></> },
|
||||
{ text: <>1.7.2: Updated OpenRouter models</> },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -13,9 +13,22 @@ export const ROUTE_INDEX = '/';
|
||||
export const ROUTE_APP_CHAT = '/';
|
||||
export const ROUTE_APP_LINK_CHAT = '/link/chat/:linkId';
|
||||
export const ROUTE_APP_NEWS = '/news';
|
||||
const ROUTE_CALLBACK_OPENROUTER = '/link/callback_openrouter';
|
||||
|
||||
export const getIndexLink = () => ROUTE_INDEX;
|
||||
|
||||
export const getCallbackUrl = (source: 'openrouter') => {
|
||||
const callbackUrl = new URL(window.location.href);
|
||||
switch (source) {
|
||||
case 'openrouter':
|
||||
callbackUrl.pathname = ROUTE_CALLBACK_OPENROUTER;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown source: ${source}`);
|
||||
}
|
||||
return callbackUrl.toString();
|
||||
};
|
||||
|
||||
export const getChatLinkRelativePath = (chatLinkId: string) => ROUTE_APP_LINK_CHAT.replace(':linkId', chatLinkId);
|
||||
|
||||
const navigateFn = (path: string) => (replace?: boolean): Promise<boolean> =>
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
* @fileoverview Utility functions for Markdown.
|
||||
*/
|
||||
|
||||
import { isBrowser } from '~/common/util/pwaUtils';
|
||||
|
||||
/**
|
||||
* Quick and dirty conversion of HTML tables to Markdown tables.
|
||||
* Big plus: doesn't require any dependencies.
|
||||
*/
|
||||
export function htmlTableToMarkdown(html: string): string {
|
||||
export function htmlTableToMarkdown(html: string, includeInvisible: boolean): string {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(html, 'text/html');
|
||||
const table = doc.querySelector('table');
|
||||
@@ -16,20 +18,53 @@ export function htmlTableToMarkdown(html: string): string {
|
||||
const headerCells = table.querySelectorAll('thead th');
|
||||
if (headerCells.length > 0) {
|
||||
const headerRow = '| ' + Array.from(headerCells)
|
||||
.map(cell => cell.textContent?.trim() || '')
|
||||
.join(' | ') + '| ';
|
||||
.map(cell => getTextWithSpaces(cell, includeInvisible).trim())
|
||||
.join(' | ') + ' |';
|
||||
markdownRows.push(headerRow);
|
||||
markdownRows.push('|:' + Array(headerCells.length).fill('-').join('|:') + '|');
|
||||
markdownRows.push('|:' + Array(headerCells.length).fill('---').join('|:') + '|');
|
||||
}
|
||||
|
||||
const bodyRows = table.querySelectorAll('tbody tr');
|
||||
for (const row of Array.from(bodyRows)) {
|
||||
const rowCells = row.querySelectorAll('td');
|
||||
const markdownRow = '| ' + Array.from(rowCells)
|
||||
.map(cell => cell.textContent?.trim() || '')
|
||||
.map(cell => getTextWithSpaces(cell, includeInvisible).trim())
|
||||
.join(' | ') + ' |';
|
||||
markdownRows.push(markdownRow);
|
||||
}
|
||||
|
||||
return markdownRows.join('\n');
|
||||
}
|
||||
|
||||
// Helper function to get text with spaces, ignoring hidden elements
|
||||
function getTextWithSpaces(node: Node, includeInvisible: boolean): string {
|
||||
let text = '';
|
||||
node.childNodes.forEach(child => {
|
||||
if (child.nodeType === Node.TEXT_NODE)
|
||||
text += child.textContent;
|
||||
else if (child.nodeType === Node.ELEMENT_NODE)
|
||||
if (includeInvisible || isVisible(child as Element))
|
||||
text += ' ' + getTextWithSpaces(child, includeInvisible) + ' ';
|
||||
});
|
||||
return text;
|
||||
}
|
||||
|
||||
// Helper function to determine if an element is visible
|
||||
function isVisible(element: Element): boolean {
|
||||
if (!isBrowser) return true;
|
||||
|
||||
// if the cell is hidden, don't include it
|
||||
const style = window.getComputedStyle(element);
|
||||
if (style.display === 'none' || style.visibility === 'hidden')
|
||||
return false;
|
||||
|
||||
// Check for common classes used to hide content or indicate tooltip/popover content.
|
||||
// You may need to add more classes here based on your actual HTML/CSS.
|
||||
const ignoredClasses = ['hidden', 'group-hover', 'tooltip', 'pointer-events-none', 'opacity-0'];
|
||||
for (const ignoredClass of ignoredClasses)
|
||||
if (element.classList.contains(ignoredClass))
|
||||
return false;
|
||||
|
||||
// Otherwise, the element is considered visible
|
||||
return true;
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { createTRPCRouter, publicProcedure } from '~/server/api/trpc.server';
|
||||
import { env } from '~/server/env.mjs';
|
||||
import { fetchJsonOrTRPCError } from '~/server/api/trpc.serverutils';
|
||||
|
||||
import { analyticsListCapabilities } from './backend.analytics';
|
||||
|
||||
@@ -30,4 +33,17 @@ export const backendRouter = createTRPCRouter({
|
||||
};
|
||||
}),
|
||||
|
||||
|
||||
// The following are used for various OAuth integrations
|
||||
|
||||
/* Exchange the OpenrRouter 'code' (from PKCS) for an OpenRouter API Key */
|
||||
exchangeOpenRouterKey: publicProcedure
|
||||
.input(z.object({ code: z.string() }))
|
||||
.query(async ({ ctx, input }) => {
|
||||
// Documented here: https://openrouter.ai/docs#oauth
|
||||
return await fetchJsonOrTRPCError<{ key: string }, { code: string }>('https://openrouter.ai/api/v1/auth/keys', 'POST', {}, {
|
||||
code: input.code,
|
||||
}, 'Backend.exchangeOpenRouterKey');
|
||||
}),
|
||||
|
||||
});
|
||||
@@ -2,7 +2,8 @@ import { create } from 'zustand';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import { persist } from 'zustand/middleware';
|
||||
|
||||
import { ModelVendorId } from './vendors/IModelVendor';
|
||||
import type { ModelVendorId } from './vendors/IModelVendor';
|
||||
import type { SourceSetupOpenRouter } from './vendors/openrouter/openrouter.vendor';
|
||||
|
||||
|
||||
/**
|
||||
@@ -76,6 +77,9 @@ interface ModelsActions {
|
||||
setChatLLMId: (id: DLLMId | null) => void;
|
||||
setFastLLMId: (id: DLLMId | null) => void;
|
||||
setFuncLLMId: (id: DLLMId | null) => void;
|
||||
|
||||
// special
|
||||
setOpenRoutersKey: (key: string) => void;
|
||||
}
|
||||
|
||||
type LlmsStore = ModelsData & ModelsActions;
|
||||
@@ -162,13 +166,22 @@ export const useModelsStore = create<LlmsStore>()(
|
||||
set(state => ({
|
||||
sources: state.sources.map((source: DModelSource): DModelSource =>
|
||||
source.id === id
|
||||
? {
|
||||
...source,
|
||||
setup: { ...source.setup, ...partialSetup },
|
||||
} : source,
|
||||
? { ...source, setup: { ...source.setup, ...partialSetup } }
|
||||
: source,
|
||||
),
|
||||
})),
|
||||
|
||||
setOpenRoutersKey: (key: string) =>
|
||||
set(state => {
|
||||
const openRouterSource = state.sources.find(source => source.vId === 'openrouter');
|
||||
if (!openRouterSource) return state;
|
||||
return {
|
||||
sources: state.sources.map(source => source.id === openRouterSource.id
|
||||
? { ...source, setup: { ...source.setup, oaiKey: key satisfies SourceSetupOpenRouter['oaiKey'] } }
|
||||
: source),
|
||||
};
|
||||
}),
|
||||
|
||||
}),
|
||||
{
|
||||
name: 'app-models',
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { z } from 'zod';
|
||||
import { TRPCError } from '@trpc/server';
|
||||
|
||||
import { createTRPCRouter, publicProcedure } from '~/server/api/trpc.server';
|
||||
import { env } from '~/server/env.mjs';
|
||||
@@ -246,8 +247,17 @@ export const llmOllamaRouter = createTRPCRouter({
|
||||
const wireGeneration = await ollamaPOST(access, ollamaChatCompletionPayload(model, history, false), OLLAMA_PATH_CHAT);
|
||||
const generation = wireOllamaChunkedOutputSchema.parse(wireGeneration);
|
||||
|
||||
if ('error' in generation)
|
||||
throw new TRPCError({
|
||||
code: 'INTERNAL_SERVER_ERROR',
|
||||
message: `Ollama chat-generation issue: ${generation.error}`,
|
||||
});
|
||||
|
||||
if (!generation.message?.content)
|
||||
throw new Error('Ollama chat generation (non-stream) issue: ' + JSON.stringify(wireGeneration));
|
||||
throw new TRPCError({
|
||||
code: 'INTERNAL_SERVER_ERROR',
|
||||
message: `Ollama chat-generation API issue: ${JSON.stringify(wireGeneration)}`,
|
||||
});
|
||||
|
||||
return {
|
||||
role: 'assistant',
|
||||
|
||||
@@ -43,27 +43,34 @@ export type WireOllamaChatCompletionInput = z.infer<typeof wireOllamaChatComplet
|
||||
/**
|
||||
* Chat Completion or Generation APIs - Streaming Response
|
||||
*/
|
||||
export const wireOllamaChunkedOutputSchema = z.object({
|
||||
model: z.string(),
|
||||
// created_at: z.string(), // commented because unused
|
||||
export const wireOllamaChunkedOutputSchema = z.union([
|
||||
// Chat Completion Chunk
|
||||
z.object({
|
||||
model: z.string(),
|
||||
// created_at: z.string(), // commented because unused
|
||||
|
||||
// [Chat Completion] (exclusive with 'response')
|
||||
message: z.object({
|
||||
role: z.enum(['assistant' /*, 'system', 'user' Disabled on purpose, to validate the response */]),
|
||||
content: z.string(),
|
||||
}).optional(), // optional on the last message
|
||||
// [Chat Completion] (exclusive with 'response')
|
||||
message: z.object({
|
||||
role: z.enum(['assistant' /*, 'system', 'user' Disabled on purpose, to validate the response */]),
|
||||
content: z.string(),
|
||||
}).optional(), // optional on the last message
|
||||
|
||||
// [Generation] (non-chat, exclusive with 'message')
|
||||
//response: z.string().optional(),
|
||||
// [Generation] (non-chat, exclusive with 'message')
|
||||
//response: z.string().optional(),
|
||||
|
||||
done: z.boolean(),
|
||||
done: z.boolean(),
|
||||
|
||||
// only on the last message
|
||||
// context: z.array(z.number()), // non-chat endpoint
|
||||
// total_duration: z.number(),
|
||||
// prompt_eval_count: z.number(),
|
||||
// prompt_eval_duration: z.number(),
|
||||
// eval_count: z.number(),
|
||||
// eval_duration: z.number(),
|
||||
// only on the last message
|
||||
// context: z.array(z.number()), // non-chat endpoint
|
||||
// total_duration: z.number(),
|
||||
// prompt_eval_count: z.number(),
|
||||
// prompt_eval_duration: z.number(),
|
||||
// eval_count: z.number(),
|
||||
// eval_duration: z.number(),
|
||||
|
||||
});
|
||||
}),
|
||||
// Possible Error
|
||||
z.object({
|
||||
error: z.string(),
|
||||
}),
|
||||
]);
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { ModelDescriptionSchema } from '../server.schemas';
|
||||
import { LLM_IF_OAI_Chat, LLM_IF_OAI_Complete, LLM_IF_OAI_Fn, LLM_IF_OAI_Vision } from '../../../store-llms';
|
||||
import { SERVER_DEBUG_WIRE } from '~/server/wire';
|
||||
|
||||
|
||||
// [Azure] / [OpenAI]
|
||||
@@ -236,8 +237,8 @@ export function oobaboogaModelToModelDescription(modelId: string, created: numbe
|
||||
/**
|
||||
* Created to reflect the doc page: https://openrouter.ai/docs
|
||||
*
|
||||
* Update prompt:
|
||||
* "Please update the typescript object below (do not change the definition, just the object), based on the updated upstream documentation:"
|
||||
* Update prompt (last updated 2023-12-12)
|
||||
* "Please update the following typescript object (do not change the definition, just values, and do not miss any rows), based on the information provided thereafter:"
|
||||
*
|
||||
* fields:
|
||||
* - cw: context window size (max tokens, total)
|
||||
@@ -247,19 +248,24 @@ export function oobaboogaModelToModelDescription(modelId: string, created: numbe
|
||||
*/
|
||||
const orModelMap: { [id: string]: { name: string; cw: number; cp?: number; cc?: number; old?: boolean; unfilt?: boolean; } } = {
|
||||
// 'openrouter/auto': { name: 'Auto (best for prompt)', cw: 128000, cp: undefined, cc: undefined, unfilt: undefined },
|
||||
'mistralai/mistral-7b-instruct': { name: 'Mistral 7B Instruct (beta)', cw: 8192, cp: 0, cc: 0, unfilt: true },
|
||||
'huggingfaceh4/zephyr-7b-beta': { name: 'Hugging Face: Zephyr 7B (beta)', cw: 4096, cp: 0, cc: 0, unfilt: true },
|
||||
'openchat/openchat-7b': { name: 'OpenChat 7B (beta)', cw: 8192, cp: 0, cc: 0, unfilt: true },
|
||||
'undi95/toppy-m-7b': { name: 'Toppy M 7B (beta)', cw: 32768, cp: 0, cc: 0, unfilt: true },
|
||||
'gryphe/mythomist-7b': { name: 'MythoMist 7B (beta)', cw: 32768, cp: 0, cc: 0, unfilt: true },
|
||||
'nousresearch/nous-hermes-llama2-13b': { name: 'Nous: Hermes 13B (beta)', cw: 4096, cp: 0.000155, cc: 0.000155, unfilt: true },
|
||||
'meta-llama/codellama-34b-instruct': { name: 'Meta: CodeLlama 34B Instruct (beta)', cw: 8192, cp: 0.00045, cc: 0.00045, unfilt: true },
|
||||
'phind/phind-codellama-34b': { name: 'Phind: CodeLlama 34B v2 (beta)', cw: 4096, cp: 0.00045, cc: 0.00045, unfilt: true },
|
||||
'intel/neural-chat-7b': { name: 'Neural Chat 7B v3.1 (beta)', cw: 32768, cp: 0.005, cc: 0.005, unfilt: true },
|
||||
'haotian-liu/llava-13b': { name: 'Llava 13B (beta)', cw: 2048, cp: 0.005, cc: 0.005, unfilt: true },
|
||||
'meta-llama/llama-2-13b-chat': { name: 'Meta: Llama v2 13B Chat (beta)', cw: 4096, cp: 0.000234533, cc: 0.000234533, unfilt: true },
|
||||
'alpindale/goliath-120b': { name: 'Goliath 120B (beta)', cw: 6144, cp: 0.00703125, cc: 0.00703125, unfilt: true },
|
||||
'lizpreciatior/lzlv-70b-fp16-hf': { name: 'lzlv 70B (beta)', cw: 4096, cp: 0.000562, cc: 0.000762, unfilt: true },
|
||||
'nousresearch/nous-capybara-7b': { name: 'Nous: Capybara 7B', cw: 4096, cp: 0, cc: 0, unfilt: true },
|
||||
'mistralai/mistral-7b-instruct': { name: 'Mistral 7B Instruct', cw: 8192, cp: 0, cc: 0, unfilt: true },
|
||||
'huggingfaceh4/zephyr-7b-beta': { name: 'Hugging Face: Zephyr 7B', cw: 4096, cp: 0, cc: 0, unfilt: true },
|
||||
'openchat/openchat-7b': { name: 'OpenChat 3.5', cw: 8192, cp: 0, cc: 0, unfilt: true },
|
||||
'gryphe/mythomist-7b': { name: 'MythoMist 7B', cw: 32768, cp: 0, cc: 0, unfilt: true },
|
||||
'openrouter/cinematika-7b': { name: 'Cinematika 7B (alpha)', cw: 32000, cp: 0, cc: 0, unfilt: true },
|
||||
'mistralai/mixtral-8x7b-instruct': { name: 'Mistral: Mixtral 8x7B Instruct (beta)', cw: 32000, cp: 0, cc: 0, unfilt: true },
|
||||
'rwkv/rwkv-5-world-3b': { name: 'RWKV v5 World 3B (beta)', cw: 10000, cp: 0, cc: 0, unfilt: true },
|
||||
'recursal/rwkv-5-3b-ai-town': { name: 'RWKV v5 3B AI Town (beta)', cw: 10000, cp: 0, cc: 0, unfilt: true },
|
||||
'jebcarter/psyfighter-13b': { name: 'Psyfighter 13B', cw: 4096, cp: 0.001, cc: 0.001, unfilt: true },
|
||||
'koboldai/psyfighter-13b-2': { name: 'Psyfighter v2 13B', cw: 4096, cp: 0.001, cc: 0.001, unfilt: true },
|
||||
'nousresearch/nous-hermes-llama2-13b': { name: 'Nous: Hermes 13B', cw: 4096, cp: 0.000075, cc: 0.000075, unfilt: true },
|
||||
'meta-llama/codellama-34b-instruct': { name: 'Meta: CodeLlama 34B Instruct', cw: 8192, cp: 0.0002, cc: 0.0002, unfilt: true },
|
||||
'phind/phind-codellama-34b': { name: 'Phind: CodeLlama 34B v2', cw: 4096, cp: 0.0002, cc: 0.0002, unfilt: true },
|
||||
'intel/neural-chat-7b': { name: 'Neural Chat 7B v3.1', cw: 4096, cp: 0.0025, cc: 0.0025, unfilt: true },
|
||||
'haotian-liu/llava-13b': { name: 'Llava 13B', cw: 2048, cp: 0.0025, cc: 0.0025, unfilt: true },
|
||||
'nousresearch/nous-hermes-2-vision-7b': { name: 'Nous: Hermes 2 Vision 7B (alpha)', cw: 4096, cp: 0.0025, cc: 0.0025, unfilt: true },
|
||||
'meta-llama/llama-2-13b-chat': { name: 'Meta: Llama v2 13B Chat', cw: 4096, cp: 0.000156755, cc: 0.000156755, unfilt: true },
|
||||
'openai/gpt-3.5-turbo': { name: 'OpenAI: GPT-3.5 Turbo', cw: 4095, cp: 0.001, cc: 0.002, unfilt: false },
|
||||
'openai/gpt-3.5-turbo-1106': { name: 'OpenAI: GPT-3.5 Turbo 16k (preview)', cw: 16385, cp: 0.001, cc: 0.002, unfilt: false },
|
||||
'openai/gpt-3.5-turbo-16k': { name: 'OpenAI: GPT-3.5 Turbo 16k', cw: 16385, cp: 0.003, cc: 0.004, unfilt: false },
|
||||
@@ -272,24 +278,38 @@ const orModelMap: { [id: string]: { name: string; cw: number; cp?: number; cc?:
|
||||
'google/palm-2-codechat-bison': { name: 'Google: PaLM 2 Code Chat', cw: 7168, cp: 0.0005, cc: 0.0005, unfilt: true },
|
||||
'google/palm-2-chat-bison-32k': { name: 'Google: PaLM 2 Chat 32k', cw: 32000, cp: 0.0005, cc: 0.0005, unfilt: true },
|
||||
'google/palm-2-codechat-bison-32k': { name: 'Google: PaLM 2 Code Chat 32k', cw: 32000, cp: 0.0005, cc: 0.0005, unfilt: true },
|
||||
'meta-llama/llama-2-70b-chat': { name: 'Meta: Llama v2 70B Chat (beta)', cw: 4096, cp: 0.0007, cc: 0.00095, unfilt: true },
|
||||
'nousresearch/nous-hermes-llama2-70b': { name: 'Nous: Hermes 70B (beta)', cw: 4096, cp: 0.0009, cc: 0.0009, unfilt: true },
|
||||
'nousresearch/nous-capybara-34b': { name: 'Nous: Capybara 34B (beta)', cw: 32000, cp: 0.02, cc: 0.02, unfilt: true },
|
||||
'jondurbin/airoboros-l2-70b': { name: 'Airoboros 70B (beta)', cw: 4096, cp: 0.0007, cc: 0.00095, unfilt: true },
|
||||
'migtissera/synthia-70b': { name: 'Synthia 70B (beta)', cw: 8192, cp: 0.009375, cc: 0.009375, unfilt: true },
|
||||
'open-orca/mistral-7b-openorca': { name: 'Mistral OpenOrca 7B (beta)', cw: 8192, cp: 0.0002, cc: 0.0002, unfilt: true },
|
||||
'teknium/openhermes-2-mistral-7b': { name: 'OpenHermes 2 Mistral 7B (beta)', cw: 4096, cp: 0.0002, cc: 0.0002, unfilt: true },
|
||||
'teknium/openhermes-2.5-mistral-7b': { name: 'OpenHermes 2.5 Mistral 7B (beta)', cw: 4096, cp: 0.0002, cc: 0.0002, unfilt: true },
|
||||
'pygmalionai/mythalion-13b': { name: 'Pygmalion: Mythalion 13B (beta)', cw: 8192, cp: 0.001125, cc: 0.001125, unfilt: true },
|
||||
'undi95/remm-slerp-l2-13b': { name: 'ReMM SLERP 13B (beta)', cw: 6144, cp: 0.001125, cc: 0.001125, unfilt: true },
|
||||
'xwin-lm/xwin-lm-70b': { name: 'Xwin 70B (beta)', cw: 8192, cp: 0.009375, cc: 0.009375, unfilt: true },
|
||||
'gryphe/mythomax-l2-13b-8k': { name: 'MythoMax 13B 8k (beta)', cw: 8192, cp: 0.001125, cc: 0.001125, unfilt: true },
|
||||
'neversleep/noromaid-20b': { name: 'Noromaid 20B (beta)', cw: 8192, cp: 0.00225, cc: 0.00225, unfilt: true },
|
||||
'perplexity/pplx-70b-online': { name: 'Perplexity: PPLX 70B Online', cw: 4096, cp: 0, cc: 0.0028, unfilt: true },
|
||||
'perplexity/pplx-7b-online': { name: 'Perplexity: PPLX 7B Online', cw: 4096, cp: 0, cc: 0.00028, unfilt: true },
|
||||
'perplexity/pplx-7b-chat': { name: 'Perplexity: PPLX 7B Chat', cw: 8192, cp: 0.00007, cc: 0.00028, unfilt: true },
|
||||
'perplexity/pplx-70b-chat': { name: 'Perplexity: PPLX 70B Chat', cw: 4096, cp: 0.0007, cc: 0.0028, unfilt: true },
|
||||
'meta-llama/llama-2-70b-chat': { name: 'Meta: Llama v2 70B Chat', cw: 4096, cp: 0.0007, cc: 0.00095, unfilt: true },
|
||||
'nousresearch/nous-hermes-llama2-70b': { name: 'Nous: Hermes 70B', cw: 4096, cp: 0.0009, cc: 0.0009, unfilt: true },
|
||||
'nousresearch/nous-capybara-34b': { name: 'Nous: Capybara 34B', cw: 32000, cp: 0.0007, cc: 0.0028, unfilt: true },
|
||||
'jondurbin/airoboros-l2-70b': { name: 'Airoboros 70B', cw: 4096, cp: 0.0007, cc: 0.00095, unfilt: true },
|
||||
'migtissera/synthia-70b': { name: 'Synthia 70B', cw: 8192, cp: 0.00375, cc: 0.00375, unfilt: true },
|
||||
'open-orca/mistral-7b-openorca': { name: 'Mistral OpenOrca 7B', cw: 8192, cp: 0.0002, cc: 0.0002, unfilt: true },
|
||||
'teknium/openhermes-2-mistral-7b': { name: 'OpenHermes 2 Mistral 7B', cw: 4096, cp: 0.0002, cc: 0.0002, unfilt: true },
|
||||
'teknium/openhermes-2.5-mistral-7b': { name: 'OpenHermes 2.5 Mistral 7B', cw: 4096, cp: 0.0002, cc: 0.0002, unfilt: true },
|
||||
'pygmalionai/mythalion-13b': { name: 'Pygmalion: Mythalion 13B', cw: 8192, cp: 0.001125, cc: 0.001125, unfilt: true },
|
||||
'undi95/remm-slerp-l2-13b': { name: 'ReMM SLERP 13B', cw: 6144, cp: 0.001125, cc: 0.001125, unfilt: true },
|
||||
'xwin-lm/xwin-lm-70b': { name: 'Xwin 70B', cw: 8192, cp: 0.00375, cc: 0.00375, unfilt: true },
|
||||
'gryphe/mythomax-l2-13b-8k': { name: 'MythoMax 13B 8k', cw: 8192, cp: 0.001125, cc: 0.001125, unfilt: true },
|
||||
'undi95/toppy-m-7b': { name: 'Toppy M 7B', cw: 32768, cp: 0.000375, cc: 0.000375, unfilt: true },
|
||||
'alpindale/goliath-120b': { name: 'Goliath 120B', cw: 6144, cp: 0.009375, cc: 0.009375, unfilt: true },
|
||||
'lizpreciatior/lzlv-70b-fp16-hf': { name: 'lzlv 70B', cw: 4096, cp: 0.0007, cc: 0.00095, unfilt: true },
|
||||
'neversleep/noromaid-20b': { name: 'Noromaid 20B', cw: 8192, cp: 0.00225, cc: 0.00225, unfilt: true },
|
||||
'01-ai/yi-34b-chat': { name: 'Yi 34B Chat', cw: 4096, cp: 0.0008, cc: 0.0008, unfilt: true },
|
||||
'01-ai/yi-34b': { name: 'Yi 34B (base)', cw: 4096, cp: 0.0008, cc: 0.0008, unfilt: true },
|
||||
'01-ai/yi-6b': { name: 'Yi 6B (base)', cw: 4096, cp: 0.00014, cc: 0.00014, unfilt: true },
|
||||
'togethercomputer/stripedhyena-nous-7b': { name: 'StripedHyena Nous 7B', cw: 32000, cp: 0.0002, cc: 0.0002, unfilt: true },
|
||||
'togethercomputer/stripedhyena-hessian-7b': { name: 'StripedHyena Hessian 7B (base)', cw: 32000, cp: 0.0002, cc: 0.0002, unfilt: true },
|
||||
'mistralai/mixtral-8x7b': { name: 'Mistral: Mixtral 8x7B (base) (beta)', cw: 32000, cp: 0.0006, cc: 0.0006, unfilt: true },
|
||||
'anthropic/claude-2': { name: 'Anthropic: Claude v2.1', cw: 200000, cp: 0.008, cc: 0.024, unfilt: false },
|
||||
'anthropic/claude-2.0': { name: 'Anthropic: Claude v2.0', cw: 100000, cp: 0.008, cc: 0.024, unfilt: false },
|
||||
'anthropic/claude-instant-v1': { name: 'Anthropic: Claude Instant v1', cw: 100000, cp: 0.00163, cc: 0.00551, unfilt: false },
|
||||
'mancer/weaver': { name: 'Mancer: Weaver (alpha)', cw: 8000, cp: 0.0045, cc: 0.0045, unfilt: true },
|
||||
'mancer/weaver': { name: 'Mancer: Weaver (alpha)', cw: 8000, cp: 0.003375, cc: 0.003375, unfilt: true },
|
||||
'gryphe/mythomax-l2-13b': { name: 'MythoMax 13B', cw: 4096, cp: 0.0006, cc: 0.0006, unfilt: true },
|
||||
// Old models (maintained for reference)
|
||||
'openai/gpt-3.5-turbo-0301': { name: 'OpenAI: GPT-3.5 Turbo (older v0301)', cw: 4095, cp: 0.001, cc: 0.002, old: true },
|
||||
'openai/gpt-4-0314': { name: 'OpenAI: GPT-4 (older v0314)', cw: 8191, cp: 0.03, cc: 0.06, old: true },
|
||||
'openai/gpt-4-32k-0314': { name: 'OpenAI: GPT-4 32k (older v0314)', cw: 32767, cp: 0.06, cc: 0.12, old: true },
|
||||
@@ -301,7 +321,12 @@ const orModelMap: { [id: string]: { name: string; cw: number; cp?: number; cc?:
|
||||
'anthropic/claude-instant-1.0': { name: 'Anthropic: Claude Instant (older v1)', cw: 9000, cp: 0.00163, cc: 0.00551, old: true },
|
||||
};
|
||||
|
||||
const orModelFamilyOrder = ['mistralai/', 'huggingfaceh4/', 'undi95/', 'openchat/', 'anthropic/', 'google/', 'openai/', 'meta-llama/', 'phind/', 'openrouter/'];
|
||||
const orModelFamilyOrder = [
|
||||
// great models
|
||||
'mistralai/mixtral-8x7b-instruct', 'mistralai/mistral-7b-instruct', 'nousresearch/nous-capybara-7b',
|
||||
// great orgs
|
||||
'huggingfaceh4/', 'openchat/', 'anthropic/', 'google/', 'openai/', 'meta-llama/', 'phind/',
|
||||
];
|
||||
|
||||
export function openRouterModelFamilySortFn(a: { id: string }, b: { id: string }): number {
|
||||
const aPrefixIndex = orModelFamilyOrder.findIndex(prefix => a.id.startsWith(prefix));
|
||||
@@ -321,10 +346,10 @@ export function openRouterModelToModelDescription(modelId: string, created: numb
|
||||
const orModel = orModelMap[modelId] ?? null;
|
||||
let label = orModel?.name || modelId.replace('/', ' · ');
|
||||
if (orModel?.cp === 0 && orModel?.cc === 0)
|
||||
label += ' - 🎁 Free';
|
||||
label += ' · 🎁 Free';
|
||||
|
||||
// if (!orModel)
|
||||
// console.log('openRouterModelToModelDescription: unknown model id:', modelId);
|
||||
if (SERVER_DEBUG_WIRE && !orModel)
|
||||
console.log(' - openRouterModelToModelDescription: non-mapped model id:', modelId);
|
||||
|
||||
// context: use the known size if available, otherwise fallback to the (undocumneted) provided length or fallback again to 4096
|
||||
const contextWindow = orModel?.cw || context_length || 4096;
|
||||
|
||||
@@ -153,6 +153,10 @@ function createOllamaChatCompletionStreamParser(): AIStreamParser {
|
||||
// validate chunk
|
||||
const chunk = wireOllamaChunkedOutputSchema.parse(wireJsonChunk);
|
||||
|
||||
// pass through errors from Ollama
|
||||
if ('error' in chunk)
|
||||
throw new Error(chunk.error);
|
||||
|
||||
// process output
|
||||
let text = chunk.message?.content || /*chunk.response ||*/ '';
|
||||
|
||||
@@ -255,7 +259,7 @@ function createEventStreamTransformer(vendorTextParser: AIStreamParser, inputFor
|
||||
} catch (error: any) {
|
||||
if (SERVER_DEBUG_WIRE)
|
||||
console.log(' - E: parse issue:', event.data, error?.message || error);
|
||||
controller.enqueue(textEncoder.encode(`[Stream Issue] ${dialectLabel}: ${safeErrorString(error) || 'Unknown stream parsing error'}`));
|
||||
controller.enqueue(textEncoder.encode(` **[Stream Issue] ${dialectLabel}: ${safeErrorString(error) || 'Unknown stream parsing error'}**`));
|
||||
controller.terminate();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { Typography } from '@mui/joy';
|
||||
import { Button, Typography } from '@mui/joy';
|
||||
|
||||
import { FormInputKey } from '~/common/components/forms/FormInputKey';
|
||||
import { InlineError } from '~/common/components/InlineError';
|
||||
import { Link } from '~/common/components/Link';
|
||||
import { SetupFormRefetchButton } from '~/common/components/forms/SetupFormRefetchButton';
|
||||
import { apiQuery } from '~/common/util/trpc.client';
|
||||
import { getCallbackUrl } from '~/common/app.routes';
|
||||
|
||||
import { DModelSourceId, useModelsStore, useSourceSetup } from '../../store-llms';
|
||||
import { modelDescriptionToDLLM } from '../openai/OpenAISourceSetup';
|
||||
@@ -38,6 +39,16 @@ export function OpenRouterSourceSetup(props: { sourceId: DModelSourceId }) {
|
||||
staleTime: Infinity,
|
||||
});
|
||||
|
||||
|
||||
const handleOpenRouterLogin = () => {
|
||||
// replace the current page with the OAuth page
|
||||
const callbackUrl = getCallbackUrl('openrouter');
|
||||
const oauthUrl = 'https://openrouter.ai/auth?callback_url=' + encodeURIComponent(callbackUrl);
|
||||
window.open(oauthUrl, '_self');
|
||||
// ...bye / see you soon at the callback location...
|
||||
};
|
||||
|
||||
|
||||
return <>
|
||||
|
||||
{/*<Box sx={{ display: 'flex', gap: 1, alignItems: 'center' }}>*/}
|
||||
@@ -53,7 +64,7 @@ export function OpenRouterSourceSetup(props: { sourceId: DModelSourceId }) {
|
||||
<FormInputKey
|
||||
id='openrouter-key' label='OpenRouter API Key'
|
||||
rightLabel={<>{needsUserKey
|
||||
? !oaiKey && <Link level='body-sm' href='https://openrouter.ai/keys' target='_blank'>create key</Link>
|
||||
? !oaiKey && <Link level='body-sm' href='https://openrouter.ai/keys' target='_blank'>your keys</Link>
|
||||
: '✔️ already set in server'
|
||||
} {oaiKey && keyValid && <Link level='body-sm' href='https://openrouter.ai/activity' target='_blank'>check usage</Link>}
|
||||
</>}
|
||||
@@ -62,7 +73,14 @@ export function OpenRouterSourceSetup(props: { sourceId: DModelSourceId }) {
|
||||
placeholder='sk-or-...'
|
||||
/>
|
||||
|
||||
<SetupFormRefetchButton refetch={refetch} disabled={!shallFetchSucceed || isFetching} error={isError} />
|
||||
<SetupFormRefetchButton
|
||||
refetch={refetch} disabled={!shallFetchSucceed || isFetching} error={isError}
|
||||
leftButton={
|
||||
<Button color='neutral' variant={(needsUserKey && !keyValid) ? 'solid' : 'outlined'} onClick={handleOpenRouterLogin}>
|
||||
OpenRouter Login
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
|
||||
{isError && <InlineError error={error} />}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user