mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-04 12:56:02 +00:00
Compare commits
4 Commits
v4.10.0-be
...
v4.10.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fee7d48dc3 | ||
|
|
8811fb647f | ||
|
|
37b017459d | ||
|
|
4889a3881b |
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "langbot"
|
||||
version = "4.10.0-beta.3"
|
||||
version = "4.10.0"
|
||||
description = "Production-grade platform for building agentic IM bots"
|
||||
readme = "README.md"
|
||||
license-files = ["LICENSE"]
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
"""LangBot - Production-grade platform for building agentic IM bots"""
|
||||
|
||||
__version__ = '4.10.0-beta.3'
|
||||
__version__ = '4.10.0'
|
||||
|
||||
@@ -459,7 +459,7 @@ class PluginRuntimeConnector(ManagedRuntimeConnector):
|
||||
)
|
||||
|
||||
file_bytes = download_resp.content
|
||||
self._extract_deps_metadata(file_bytes, task_context)
|
||||
self._inspect_plugin_package(file_bytes, task_context)
|
||||
file_key = await self.handler.send_file(file_bytes, 'lbpkg')
|
||||
install_info['plugin_file_key'] = file_key
|
||||
self.ap.logger.info(f'Transfered file {file_key} to plugin runtime')
|
||||
|
||||
@@ -143,49 +143,83 @@ class ModelManager:
|
||||
# get the latest models from space
|
||||
space_models = await self.ap.space_service.get_models()
|
||||
|
||||
exists_llm_models_uuids = [m['uuid'] for m in await self.ap.llm_model_service.get_llm_models()]
|
||||
exists_embedding_models_uuids = [
|
||||
m['uuid'] for m in await self.ap.embedding_models_service.get_embedding_models()
|
||||
]
|
||||
# Index existing models by uuid. Space reuses a model's uuid across
|
||||
# renames / re-specs (e.g. the uuid that used to be ``claude-opus-4-6``
|
||||
# may later become ``claude-opus-4-7``). So for Space-managed models we
|
||||
# upsert: create when the uuid is new, otherwise update name/abilities/
|
||||
# ranking to track Space. Models owned by other providers are never
|
||||
# touched, even on an (unexpected) uuid collision.
|
||||
existing_llm_models = {m['uuid']: m for m in await self.ap.llm_model_service.get_llm_models()}
|
||||
existing_embedding_models = {
|
||||
m['uuid']: m for m in await self.ap.embedding_models_service.get_embedding_models()
|
||||
}
|
||||
|
||||
created = 0
|
||||
updated = 0
|
||||
|
||||
for space_model in space_models:
|
||||
if space_model.category == 'chat':
|
||||
uuid = space_model.uuid
|
||||
|
||||
if uuid in exists_llm_models_uuids:
|
||||
continue
|
||||
|
||||
# model will be automatically loaded
|
||||
await self.ap.llm_model_service.create_llm_model(
|
||||
{
|
||||
'uuid': space_model.uuid,
|
||||
existing = existing_llm_models.get(space_model.uuid)
|
||||
if existing is None:
|
||||
# model will be automatically loaded
|
||||
await self.ap.llm_model_service.create_llm_model(
|
||||
{
|
||||
'uuid': space_model.uuid,
|
||||
'name': space_model.model_id,
|
||||
'provider_uuid': space_model_provider.uuid,
|
||||
'abilities': space_model.llm_abilities or [],
|
||||
'extra_args': {},
|
||||
'prefered_ranking': space_model.featured_order,
|
||||
},
|
||||
preserve_uuid=True,
|
||||
auto_set_to_default_pipeline=False,
|
||||
)
|
||||
created += 1
|
||||
elif existing.get('provider_uuid') == space_model_provider.uuid:
|
||||
desired = {
|
||||
'name': space_model.model_id,
|
||||
'provider_uuid': space_model_provider.uuid,
|
||||
'abilities': space_model.llm_abilities or [],
|
||||
'extra_args': {},
|
||||
'prefered_ranking': space_model.featured_order,
|
||||
},
|
||||
preserve_uuid=True,
|
||||
auto_set_to_default_pipeline=False,
|
||||
)
|
||||
}
|
||||
if (
|
||||
existing.get('name') != desired['name']
|
||||
or list(existing.get('abilities') or []) != list(desired['abilities'])
|
||||
or existing.get('prefered_ranking') != desired['prefered_ranking']
|
||||
):
|
||||
await self.ap.llm_model_service.update_llm_model(space_model.uuid, dict(desired))
|
||||
updated += 1
|
||||
|
||||
elif space_model.category == 'embedding':
|
||||
uuid = space_model.uuid
|
||||
|
||||
if uuid in exists_embedding_models_uuids:
|
||||
continue
|
||||
|
||||
# model will be automatically loaded
|
||||
await self.ap.embedding_models_service.create_embedding_model(
|
||||
{
|
||||
'uuid': space_model.uuid,
|
||||
existing = existing_embedding_models.get(space_model.uuid)
|
||||
if existing is None:
|
||||
# model will be automatically loaded
|
||||
await self.ap.embedding_models_service.create_embedding_model(
|
||||
{
|
||||
'uuid': space_model.uuid,
|
||||
'name': space_model.model_id,
|
||||
'provider_uuid': space_model_provider.uuid,
|
||||
'extra_args': {},
|
||||
'prefered_ranking': space_model.featured_order,
|
||||
},
|
||||
preserve_uuid=True,
|
||||
)
|
||||
created += 1
|
||||
elif existing.get('provider_uuid') == space_model_provider.uuid:
|
||||
desired = {
|
||||
'name': space_model.model_id,
|
||||
'provider_uuid': space_model_provider.uuid,
|
||||
'extra_args': {},
|
||||
'prefered_ranking': space_model.featured_order,
|
||||
},
|
||||
preserve_uuid=True,
|
||||
)
|
||||
}
|
||||
if (
|
||||
existing.get('name') != desired['name']
|
||||
or existing.get('prefered_ranking') != desired['prefered_ranking']
|
||||
):
|
||||
await self.ap.embedding_models_service.update_embedding_model(space_model.uuid, dict(desired))
|
||||
updated += 1
|
||||
|
||||
if created or updated:
|
||||
self.ap.logger.info(f'Synced models from LangBot Space: {created} added, {updated} updated.')
|
||||
|
||||
async def init_temporary_runtime_llm_model(
|
||||
self,
|
||||
|
||||
2
uv.lock
generated
2
uv.lock
generated
@@ -1899,7 +1899,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "langbot"
|
||||
version = "4.10.0b3"
|
||||
version = "4.10.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "aiocqhttp" },
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from '@/components/ui/popover';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ScannedProviderModel } from '@/app/infra/entities/api';
|
||||
import {
|
||||
@@ -298,20 +298,8 @@ export default function AddModelPopover({
|
||||
</div>
|
||||
|
||||
<div className="overflow-y-auto flex-1 min-h-0">
|
||||
<Tabs
|
||||
value={mode}
|
||||
onValueChange={(v) => setMode(v as 'manual' | 'scan')}
|
||||
>
|
||||
{!trigger && (
|
||||
<TabsList className="grid w-full grid-cols-2 mt-3">
|
||||
<TabsTrigger value="manual">
|
||||
{t('models.manualAdd')}
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="scan">{t('models.scanAdd')}</TabsTrigger>
|
||||
</TabsList>
|
||||
)}
|
||||
|
||||
<TabsContent value="manual" className="mt-3">
|
||||
{mode === 'manual' ? (
|
||||
<div className="mt-3">
|
||||
<div className="space-y-3">
|
||||
<div className="space-y-2">
|
||||
<Label>{t('models.modelName')}</Label>
|
||||
@@ -390,9 +378,9 @@ export default function AddModelPopover({
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="scan" className="space-y-2 mt-0 pt-0">
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-2 mt-3">
|
||||
{scanLoading ? (
|
||||
<div className="flex items-center justify-center py-4">
|
||||
<RefreshCw className="h-4 w-4 mr-2 animate-spin text-muted-foreground" />
|
||||
@@ -565,8 +553,8 @@ export default function AddModelPopover({
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Tabs>
|
||||
</PopoverContent>
|
||||
|
||||
Reference in New Issue
Block a user