diff --git a/src/langbot/pkg/persistence/mgr.py b/src/langbot/pkg/persistence/mgr.py index 53b20011..92be6a77 100644 --- a/src/langbot/pkg/persistence/mgr.py +++ b/src/langbot/pkg/persistence/mgr.py @@ -9,7 +9,7 @@ import sqlalchemy.ext.asyncio as sqlalchemy_asyncio import sqlalchemy from . import database, migration -from ..entity.persistence import base, pipeline, metadata +from ..entity.persistence import base, pipeline, metadata, model as persistence_model from ..entity import persistence from ..core import app from ..utils import constants, importutil @@ -79,6 +79,7 @@ class PersistenceManager: self.ap.logger.info(f'Successfully upgraded database to version {last_migration_number}.') await self.write_default_pipeline() + await self.write_space_model_providers() async def create_tables(self): # create tables @@ -123,7 +124,28 @@ class PersistenceManager: await self.execute_async(sqlalchemy.insert(pipeline.LegacyPipeline).values(pipeline_data)) - # ================================= + async def write_space_model_providers(self): + # write space model providers + result = await self.execute_async( + sqlalchemy.select(persistence_model.ModelProvider).where( + persistence_model.ModelProvider.requester == 'space-chat-completions' + ) + ) + if result.first() is None: + self.ap.logger.info('Creating space model providers...') + space_chat_completions_model_provider = { + 'uuid': str(uuid.uuid4()), + 'name': 'LangBot Models', + 'requester': 'space-chat-completions', + 'base_url': 'https://api.langbot.cloud/v1', + 'api_keys': [], + } + + await self.execute_async( + sqlalchemy.insert(persistence_model.ModelProvider).values(space_chat_completions_model_provider) + ) + + # ================================= async def execute_async(self, *args, **kwargs) -> sqlalchemy.engine.cursor.CursorResult: async with self.get_db_engine().connect() as conn: diff --git a/src/langbot/pkg/provider/modelmgr/requesters/space.webp b/src/langbot/pkg/provider/modelmgr/requesters/space.webp new file mode 100644 index 00000000..09226adc Binary files /dev/null and b/src/langbot/pkg/provider/modelmgr/requesters/space.webp differ diff --git a/src/langbot/pkg/provider/modelmgr/requesters/spacechatcmpl.py b/src/langbot/pkg/provider/modelmgr/requesters/spacechatcmpl.py new file mode 100644 index 00000000..91740a1f --- /dev/null +++ b/src/langbot/pkg/provider/modelmgr/requesters/spacechatcmpl.py @@ -0,0 +1,17 @@ +from __future__ import annotations + +import typing +import openai + +from . import chatcmpl + + +class LangBotSpaceChatCompletions(chatcmpl.OpenAIChatCompletions): + """LangBot Space ChatCompletion API 请求器""" + + client: openai.AsyncClient + + default_config: dict[str, typing.Any] = { + 'base_url': 'https://api.langbot.cloud/v1', + 'timeout': 120, + } diff --git a/src/langbot/pkg/provider/modelmgr/requesters/spacechatcmpl.yaml b/src/langbot/pkg/provider/modelmgr/requesters/spacechatcmpl.yaml new file mode 100644 index 00000000..29c23a83 --- /dev/null +++ b/src/langbot/pkg/provider/modelmgr/requesters/spacechatcmpl.yaml @@ -0,0 +1,32 @@ +apiVersion: v1 +kind: LLMAPIRequester +metadata: + name: space-chat-completions + label: + en_US: Space + zh_Hans: Space + icon: space.webp +spec: + config: + - name: base_url + label: + en_US: Base URL + zh_Hans: 基础 URL + type: string + required: true + default: https://api.langbot.cloud/v1 + - name: timeout + label: + en_US: Timeout + zh_Hans: 超时时间 + type: integer + required: true + default: 120 + support_type: + - llm + - text-embedding + provider_category: maas +execution: + python: + path: ./spacechatcmpl.py + attr: LangBotSpaceChatCompletions diff --git a/web/src/app/home/components/models-dialog/ModelsDialog.tsx b/web/src/app/home/components/models-dialog/ModelsDialog.tsx index 6579ca9c..4a9404b3 100644 --- a/web/src/app/home/components/models-dialog/ModelsDialog.tsx +++ b/web/src/app/home/components/models-dialog/ModelsDialog.tsx @@ -53,6 +53,7 @@ interface ModelsDialogProps { } const LANGBOT_MODELS_PROVIDER_NAME = 'LangBot Models'; +const LANGBOT_MODELS_PROVIDER_REQUESTER = 'space-chat-completions'; export default function ModelsDialog({ open, @@ -253,10 +254,10 @@ export default function ModelsDialog({ // Separate LangBot Models provider const langbotProvider = providers.find( - (p) => p.name === LANGBOT_MODELS_PROVIDER_NAME, + (p) => p.requester === LANGBOT_MODELS_PROVIDER_REQUESTER, ); const otherProviders = providers.filter( - (p) => p.name !== LANGBOT_MODELS_PROVIDER_NAME, + (p) => p.requester !== LANGBOT_MODELS_PROVIDER_REQUESTER, ); function renderProviderCard( @@ -501,58 +502,6 @@ export default function ModelsDialog({ if (langbotProvider) { return renderProviderCard(langbotProvider, true); } - return ( - - -
-
-
- LangBot -
-
- - {LANGBOT_MODELS_PROVIDER_NAME} - -

- {t('models.langbotModelsDescription')} -

-
-
- {accountType !== 'space' ? ( - - ) : ( - spaceCredits !== null && ( -
- - {(spaceCredits / 5000).toFixed(2)} {t('models.credits')} - - -
- ) - )} -
-
-
- ); } function handleFormClose() { diff --git a/web/src/app/home/components/models-dialog/component/embedding-form/EmbeddingForm.tsx b/web/src/app/home/components/models-dialog/component/embedding-form/EmbeddingForm.tsx index 5bd3b66a..3e7d942e 100644 --- a/web/src/app/home/components/models-dialog/component/embedding-form/EmbeddingForm.tsx +++ b/web/src/app/home/components/models-dialog/component/embedding-form/EmbeddingForm.tsx @@ -373,11 +373,15 @@ export default function EmbeddingForm({ /> - {providers.map((p) => ( - - {p.name} ({p.base_url || 'default'}) - - ))} + {providers + .filter( + (p) => p.requester !== 'space-chat-completions', + ) + .map((p) => ( + + {p.name} ({p.base_url || 'default'}) + + ))} @@ -413,7 +417,11 @@ export default function EmbeddingForm({ {t('models.modelManufacturer')} {requesterList - .filter((r) => r.category === 'manufacturer') + .filter( + (r) => + r.category === 'manufacturer' && + r.value !== 'space-chat-completions', + ) .map((r) => ( {r.label} @@ -425,7 +433,11 @@ export default function EmbeddingForm({ {t('models.aggregationPlatform')} {requesterList - .filter((r) => r.category === 'maas') + .filter( + (r) => + r.category === 'maas' && + r.value !== 'space-chat-completions', + ) .map((r) => ( {r.label} @@ -437,7 +449,11 @@ export default function EmbeddingForm({ {t('models.selfDeployed')} {requesterList - .filter((r) => r.category === 'self-hosted') + .filter( + (r) => + r.category === 'self-hosted' && + r.value !== 'space-chat-completions', + ) .map((r) => ( {r.label} diff --git a/web/src/app/home/components/models-dialog/component/llm-form/LLMForm.tsx b/web/src/app/home/components/models-dialog/component/llm-form/LLMForm.tsx index fb8a79ae..7d816d3a 100644 --- a/web/src/app/home/components/models-dialog/component/llm-form/LLMForm.tsx +++ b/web/src/app/home/components/models-dialog/component/llm-form/LLMForm.tsx @@ -385,11 +385,15 @@ export default function LLMForm({ /> - {providers.map((p) => ( - - {p.name} ({p.base_url || 'default'}) - - ))} + {providers + .filter( + (p) => p.requester !== 'space-chat-completions', + ) + .map((p) => ( + + {p.name} ({p.base_url || 'default'}) + + ))} @@ -425,7 +429,11 @@ export default function LLMForm({ {t('models.modelManufacturer')} {requesterList - .filter((r) => r.category === 'manufacturer') + .filter( + (r) => + r.category === 'manufacturer' && + r.value !== 'space-chat-completions', + ) .map((r) => ( {r.label} @@ -437,7 +445,11 @@ export default function LLMForm({ {t('models.aggregationPlatform')} {requesterList - .filter((r) => r.category === 'maas') + .filter( + (r) => + r.category === 'maas' && + r.value !== 'space-chat-completions', + ) .map((r) => ( {r.label} @@ -449,7 +461,11 @@ export default function LLMForm({ {t('models.selfDeployed')} {requesterList - .filter((r) => r.category === 'self-hosted') + .filter( + (r) => + r.category === 'self-hosted' && + r.value !== 'space-chat-completions', + ) .map((r) => ( {r.label}