* feat(packaging): add fnOS FPK packaging and CI workflow
Add packaging/fnos/ shell (manifest, lifecycle cmd scripts, install/
upgrade/uninstall wizards, desktop entry, EULA) plus in-repo build
script. A release now auto-builds langbot-<tag>-fnos.fpk via
.github/workflows/build-fnos-fpk.yaml, uploaded to the release assets.
* ci(fnos): skip release upload on manual dispatch
github.event.release.tag_name is empty when triggered via
workflow_dispatch, causing 'gh release upload' to fail with
'requires at least 2 arg(s)'. Restrict the step to release events.
* ci(fnos): normalize release asset name
Strip a trailing -fnos from the tag-derived version before appending
the suffix, avoiding langbot-<v>-fnos-fnos.fpk when the tag itself
already carries -fnos.
* ci(fnos): use release tag version for auto build, manifest for manual
Auto build (release/tag) reads version from tag like other release
workflows; build.sh strips the v prefix when injecting into manifest.
Manual dispatch falls back to the version maintained in manifest.
* feat(fnos): add post-install deployment notice to install wizard
Last wizard step now informs users that first startup takes about
5-10 minutes for dependency setup before the web UI is ready.
* docs(fnos): add packaging directory README
* refactor(fnos): rename app from ai.langbot to langbot
Rename appname, desktop entry, data share, build artifacts, and all
references from ai.langbot to langbot across packaging files.
---------
Co-authored-by: dadachann <185672915+dadachann@users.noreply.github.com>
* feat(mcp): surface OAuth-required server tests
* fix(mcp): show connection failure details in status cards
---------
Co-authored-by: RockChinQ <rockchinq@gmail.com>
create_bot inserts the Bot row first and only then instantiates the
adapter via platform_mgr.load_bot. When the adapter constructor raises
(e.g. KeyError on a missing credential key), the insert is already
committed and nothing removes the row: the HTTP layer returns 500 but
a permanently disabled orphan bot stays in the DB. Callers never
receive the bot uuid, so they cannot compensate by deleting it, and
load_bots_from_db skips enable=False bots, so the orphan is never
loaded or surfaced anywhere.
Wrap load_bot in try/except and delete the inserted row before
re-raising. Add a regression test asserting the DELETE is issued when
the adapter constructor fails.
Since b55f073e the token field is optional in qqofficial.yaml ("the
current adapter implementation does not use it either, so it can be
safely left blank"), but the adapter constructor still reads it with
config['token']. Creating a bot via QR binding (which only returns
appid/secret) or with the token field left blank raises KeyError and
the API returns 500.
Read it with config.get('token', '') instead. The value is never used
by QQOfficialClient beyond being stored, so an empty string default is
safe.
WecomMessageConverter.yiri2target() always emits {'media_id': ...} for
image/voice/file parts (never 'media'), matching the correct usage
already in reply_message(). send_message(), the entry point plugins
use via PluginToRuntimeAction.SEND_MESSAGE, instead read
content['media'], which is never set, so any image/voice/file part
raises KeyError and aborts the send.
Refs #1687
Signed-off-by: Amir Fathi <amirfathi.me@gmail.com>
* fix(cloud): provision login workspace just in time
* fix(oauth): send callback URI during code exchange
* fix(oauth): preserve callback URI through browser exchange
* fix(oauth): negotiate redirect-bound codes
---------
Co-authored-by: dadachann <185672915+dadachann@users.noreply.github.com>
* fix(cloud): launch new accounts through Space
* style: format Cloud entry URL
* fix(cloud): wait for launch workspace projection
---------
Co-authored-by: dadachann <185672915+dadachann@users.noreply.github.com>
The Ollama requester declared litellm_provider: ollama, which routes
every request through litellm's legacy /api/generate-based
OllamaConfig. That config's get_supported_openai_params() does not
include "tools"/"tool_choice" at all, so an Ollama-hosted model in a
local-agent pipeline could never receive a structured tool definition
or return a structured tool_calls response - it could only try to
express a tool call as free text (typically inside its own <think>
reasoning), which LangBot then has no way to execute.
litellm's "ollama_chat" provider targets Ollama's modern /api/chat
endpoint instead, which correctly forwards tools/tool_choice and
correctly surfaces the model's native message.tool_calls field.
Verified against a real local Ollama 0.33.2 instance with the exact
system prompt, RAG-augmented user message, and tool set a live
pipeline sends.
Two follow-on fixes needed because the Ollama requester definition is
shared by LLM and text-embedding models:
- get_reasoning_capabilities: match family in ('ollama', 'ollama_chat')
so the reasoning-level UI still works for this provider.
- scan_models: retry {base_url}/v1/models on a 404 from {base_url}/models,
since Ollama's base_url is a bare host (must not include /v1 - that
would break OllamaChatConfig.get_complete_url, which appends /api/chat
to it directly), unlike most other OpenAI-compatible providers whose
base_url already ends in /v1.
- invoke_embedding: litellm's embedding routing has no "ollama_chat"
case, only "ollama". Build the embedding model name with an explicit
custom_llm_provider="ollama" override when the requester is configured
for ollama_chat, so embedding models (e.g. bge-m3) keep working.
Co-authored-by: zx90316 <zx90316@users.noreply.github.com>