uclone-x 0.2.1__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {uclone_x-0.2.1 → uclone_x-0.2.2}/CHANGELOG.md +89 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/PKG-INFO +3 -1
- {uclone_x-0.2.1 → uclone_x-0.2.2}/pyproject.toml +7 -1
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/__init__.py +1 -1
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/agent/base.py +49 -13
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/agent/models.py +7 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/agent/prompts.py +16 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/cli/commands/a2a.py +5 -1
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/cli/commands/acp.py +6 -1
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/cli/commands/bootstrap.py +132 -22
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/cli/commands/eval.py +3 -1
- uclone_x-0.2.2/src/uclone_x/cli/commands/key.py +207 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/cli/commands/llm.py +67 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/cli/commands/loop.py +8 -1
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/cli/commands/media.py +4 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/cli/commands/room.py +23 -4
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/cli/commands/run.py +54 -9
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/cli/main.py +2 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/core/environment_install.py +1 -1
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/errors.py +21 -0
- uclone_x-0.2.2/src/uclone_x/llm/connectors/factory.py +362 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/llm/connectors/ollama.py +61 -24
- uclone_x-0.2.2/src/uclone_x/llm/connectors/saved_choice.py +346 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/llm/context_window.py +70 -28
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/llm/models.py +4 -1
- uclone_x-0.2.2/src/uclone_x/personas/guardian.yaml +37 -0
- uclone_x-0.2.2/src/uclone_x/personas/pioneer.yaml +31 -0
- uclone_x-0.2.2/src/uclone_x/personas/scout.yaml +23 -0
- uclone_x-0.2.2/src/uclone_x/personas/writer.yaml +23 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/room/models.py +9 -3
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/shells/acp/server.py +10 -2
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/tools/builtin/image.py +336 -18
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui/app.py +137 -23
- uclone_x-0.2.2/src/uclone_x/ui_build_inputs.sha256 +1 -0
- uclone_x-0.2.1/src/uclone_x/ui_static/assets/index-DOOa_O0r.js → uclone_x-0.2.2/src/uclone_x/ui_static/assets/index-B26a-pug.js +142 -142
- uclone_x-0.2.1/src/uclone_x/ui_static/assets/index-B0hbgNk5.css → uclone_x-0.2.2/src/uclone_x/ui_static/assets/index-uisJBy0U.css +1 -1
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/index.html +2 -2
- uclone_x-0.2.1/src/uclone_x/llm/connectors/factory.py +0 -155
- uclone_x-0.2.1/src/uclone_x/personas/guardian.yaml +0 -20
- uclone_x-0.2.1/src/uclone_x/personas/pioneer.yaml +0 -20
- uclone_x-0.2.1/src/uclone_x/personas/scout.yaml +0 -16
- uclone_x-0.2.1/src/uclone_x/personas/writer.yaml +0 -18
- uclone_x-0.2.1/src/uclone_x/ui_build_inputs.sha256 +0 -1
- {uclone_x-0.2.1 → uclone_x-0.2.2}/.gitignore +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/LICENSE +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/NOTICE +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/README.md +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/a2a/__init__.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/a2a/discovery.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/a2a/http_transport.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/a2a/in_memory.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/a2a/models.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/a2a/protocols.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/a2a/wire.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/acp/__init__.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/acp/conformance.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/adapters/__init__.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/adapters/uclone2/__init__.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/adapters/uclone2/adk_content.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/agent/__init__.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/agent/bootstrap.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/agent/composition.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/agent/grounding.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/agent/hooks/__init__.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/agent/hooks/artifact_validation.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/agent/hooks/models.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/agent/hooks/permission.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/agent/hooks/protocols.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/agent/hooks/runner.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/agent/hooks/script_runner.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/agent/loop/__init__.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/agent/loop/models.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/agent/loop/parser.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/agent/loop/runner.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/agent/loop/scheduler.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/agent/memory.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/agent/persona_registry.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/agent/persona_store.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/agent/planner.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/agent/protocols.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/agent/request_record.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/agent/session.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/agent/terminology.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/agent/text_tool_calls.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/agent/turn_trace.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/cli/__init__.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/cli/agent_memory.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/cli/bundle_freshness.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/cli/changed_scope.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/cli/commands/__init__.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/cli/commands/dev.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/cli/commands/ontology.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/cli/commands/report.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/cli/commands/session_dev.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/cli/commands/skill.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/cli/commands/ui.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/cli/environment_provenance.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/cli/quality_gate.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/code_intel/__init__.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/code_intel/ast_parser.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/code_intel/models.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/code_intel/protocols.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/code_intel/scip.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/code_intel/symbol_graph.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/config/default_models.yaml +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/core/__init__.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/core/agent_home.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/core/capability.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/core/client_driver.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/core/diagnostic_report.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/core/failure_journal.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/core/host.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/core/immutable.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/core/log_inspector.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/core/log_offset.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/core/log_writer.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/core/logging_setup.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/core/provenance.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/core/secrets.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/core/session_diagnostics.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/core/session_store.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/core/tool_results.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/core/workspace.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/engine/__init__.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/engine/event_bus.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/engine/mattermost_bridge.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/engine/protocols.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/evaluation/__init__.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/evaluation/answerer.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/evaluation/loader.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/evaluation/protocols.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/llm/__init__.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/llm/budget.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/llm/compactor.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/llm/connectors/__init__.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/llm/connectors/anthropic.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/llm/connectors/base.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/llm/connectors/gemini.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/llm/connectors/mock.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/llm/connectors/ollama_embedder.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/llm/connectors/openai.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/llm/connectors/vllm.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/llm/protocols.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/llm/router.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/log/__init__.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/log/file_allocator.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/log/reader.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/memory/__init__.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/memory/models.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/memory/retrieval.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/memory/store.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/memory/tools.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/memory/vector_store.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ontology/__init__.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ontology/engine.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ontology/justification.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ontology/models.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ontology/protocols.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ontology/reasoner.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ontology/rules.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/personas/artist.png +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/personas/artist.yaml +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/personas/clone.png +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/personas/clone.yaml +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/personas/guardian.png +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/personas/pioneer.png +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/personas/scout.png +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/personas/writer.png +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/room/__init__.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/room/knowledge.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/room/knowledge_store.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/room/orchestrator.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/room/protocols.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/room/resolver.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/room/selectors.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/room/service.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/room/store.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/room/turn_summary.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/sandbox/__init__.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/sandbox/models.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/sandbox/path_validator.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/sandbox/protocols.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/sandbox/workspace_runner.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/shells/a2a_server.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/shells/acp/__init__.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/shells/acp/driver.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/shells/acp/models.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/shells/entry.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/shells/ui_process.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/skills/__init__.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/skills/auditor.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/skills/models.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/skills/protocols.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/skills/synthesizer.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/telemetry/__init__.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/telemetry/exporter.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/telemetry/langfuse.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/telemetry/metrics.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/telemetry/models.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/telemetry/otel_sdk.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/telemetry/otlp.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/telemetry/protocols.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/telemetry/tracer.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/tools/__init__.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/tools/base.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/tools/builtin/__init__.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/tools/builtin/character.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/tools/builtin/comfy_client.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/tools/builtin/comfy_image_tool.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/tools/builtin/filesystem.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/tools/builtin/install.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/tools/builtin/mcp_loader.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/tools/builtin/media_registry.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/tools/builtin/plan.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/tools/builtin/shell.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/tools/builtin/skill_loader.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/tools/builtin/subagent.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/tools/builtin/tool_results.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/tools/builtin/web.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/tools/client.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/tools/mcp_manager.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/tools/models.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/tools/outcome.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/tools/protocols.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/tools/registry.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/tools/scoped_registry.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/tools/tool_scoper.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui/__init__.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui/build_inputs.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui/clones.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui/knowledge.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui/room_dock.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui/rooms.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui/server.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui/single_flight.py +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/favicon.ico +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/logo.png +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/logoWhite.png +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/logo_uclone_short.png +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/uclone_logo_1024x1024.png +0 -0
- {uclone_x-0.2.1 → uclone_x-0.2.2}/src/uclone_x/ui_static/uclone_logo_circle.png +0 -0
|
@@ -13,6 +13,95 @@ names does.
|
|
|
13
13
|
Versions follow [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Each date is
|
|
14
14
|
the date that version was published.
|
|
15
15
|
|
|
16
|
+
## [0.2.2] - 2026-09-24
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- `ucx install` and `ucx start` now save the model they set up as the default, so
|
|
21
|
+
`ucx run`, `ucx room`, `ucx loop`, `ucx acp` and `ucx a2a` use it without `--provider`
|
|
22
|
+
or `--model`, and say which saved model they are using (`ucx acp` and `ucx a2a` on
|
|
23
|
+
stderr). A model already saved, for example in the dashboard's Settings, is kept; when
|
|
24
|
+
it differs from the one setup just prepared, setup says both and how to switch. A
|
|
25
|
+
dashboard that was started with no model picks up a default saved later.
|
|
26
|
+
- `ucx llm use <model>` (with `--provider` and `--base-url`) changes the saved default.
|
|
27
|
+
It is the same setting as the dashboard's Settings. Command-line flags and environment
|
|
28
|
+
variables such as `LLM_PROVIDER`, `OLLAMA_MODEL` or a provider's API key variable still
|
|
29
|
+
come first, and `ucx llm use` says when one of them is set and takes priority.
|
|
30
|
+
- In Settings, the API key field for OpenAI, Anthropic and Gemini links to the page where
|
|
31
|
+
that provider issues keys, warns when a key does not look like one for the chosen
|
|
32
|
+
provider or confirms that its format is right, and removes spaces and one pair of quotes
|
|
33
|
+
from the ends of a pasted key.
|
|
34
|
+
- `ucx key list` shows whether `GEMINI_API_KEY` (or `GOOGLE_API_KEY`),
|
|
35
|
+
`ANTHROPIC_API_KEY` and `OPENAI_API_KEY` are set. `ucx key setup` opens the provider's
|
|
36
|
+
key page and writes the key you paste to a `.env` file: the nearest one in this folder
|
|
37
|
+
or above it, looking no higher than the top of the enclosing git checkout, where it
|
|
38
|
+
creates one if none is found. Outside a git checkout, it creates one in this folder if
|
|
39
|
+
none is found above. UClone-X does not read that file itself; load it into your shell,
|
|
40
|
+
or enter the key in Settings, for the key to be used.
|
|
41
|
+
- Every clone may load the skills approved for it, without `load_skill` in its tool list,
|
|
42
|
+
and Scout can read web pages as well as search.
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
|
|
46
|
+
- Ollama requests now ask for a 16K-token context window, unless `context_limit` or
|
|
47
|
+
`OLLAMA_CONTEXT_LENGTH` sets another, instead of leaving the size to Ollama, whose own
|
|
48
|
+
default was 4K on the machine the README's figures come from. A clone's request with
|
|
49
|
+
tool results then fits. It costs memory: for `qwen3:8b`, about 1.8 GB more than a 4K
|
|
50
|
+
window (an estimate, not a measurement).
|
|
51
|
+
- The built-in clones Scout, Pioneer, Guardian and Writer have rewritten instructions,
|
|
52
|
+
now written entirely in English; they still answer in the language you write in. With
|
|
53
|
+
a Qwen model, clones are also told not to use tools that change files or other state
|
|
54
|
+
for advice, brainstorming or general reasoning unless you ask for that change, to use
|
|
55
|
+
read-only search and inspection tools when an answer needs evidence from the workspace
|
|
56
|
+
or the web, and to answer in the language of the question. They are told that accuracy
|
|
57
|
+
requirements alone are no reason to decline a reasonable request, to follow your
|
|
58
|
+
instructions and style without imposing their own judgments, to give substantive
|
|
59
|
+
answers, and not to add moral lectures, unsolicited caveats or boilerplate disclaimers.
|
|
60
|
+
- The conversation list shows compact times ("now", "5m", "1d", "2w"), and a row's
|
|
61
|
+
rename and delete buttons appear when you point at the row or move keyboard focus to
|
|
62
|
+
it, so titles have more room.
|
|
63
|
+
|
|
64
|
+
### Fixed
|
|
65
|
+
|
|
66
|
+
- Local image generation on an NVIDIA card now runs on the GPU in half precision, where
|
|
67
|
+
0.2.1 ran it on the CPU. The engine tries CUDA, then Apple's MPS, then the CPU;
|
|
68
|
+
`ucx media status` names the device it will use, and image results name the GPU.
|
|
69
|
+
- When less than about 10 GB of GPU memory is free (an estimate), for example because
|
|
70
|
+
Ollama is keeping a model loaded on the same card, the image engine keeps SDXL's parts
|
|
71
|
+
in system memory, moves each onto the card only while it runs, and decodes the image in
|
|
72
|
+
tiles. This needs `accelerate`, which is now installed with the image engine. It is
|
|
73
|
+
slower and can still run out of memory; if it does, the image request fails with a
|
|
74
|
+
plain message and the memory is released, and when `accelerate` is missing the message
|
|
75
|
+
says how to add it.
|
|
76
|
+
- Under WSL2, the free GPU memory used for that decision now counts memory held by other
|
|
77
|
+
processes (read with NVML or `nvidia-smi`), so the image engine switches to the
|
|
78
|
+
low-memory mode when another program holds the card. When neither can be read, it uses
|
|
79
|
+
the low-memory mode.
|
|
80
|
+
- A model that Ollama cannot give tools to (for example `deepseek-r1:14b`) now ends the
|
|
81
|
+
turn with a short message naming the model and suggesting one that can use tools, such
|
|
82
|
+
as `qwen3:8b`, plus where to change it: `--model` for `ucx run`,
|
|
83
|
+
`ucx room retry <id> --model <model>` in a room, and Settings in the dashboard and in an
|
|
84
|
+
ACP client. The dashboard gives that remedy instead of offering a Retry that would fail
|
|
85
|
+
the same way.
|
|
86
|
+
- The one-line install now offers the image engine, which the published package
|
|
87
|
+
includes; 0.2.1 skipped it. When the installer reports success but the engine's core
|
|
88
|
+
packages (Pillow, diffusers, torch and transformers) cannot be imported, it says so
|
|
89
|
+
instead of reporting the engine installed. That check does not cover `accelerate`.
|
|
90
|
+
- Setting up Ollama runs its official installer up to 3 times when the download is cut
|
|
91
|
+
off, with a message between attempts. An attempt that runs out the 15-minute limit is
|
|
92
|
+
not repeated.
|
|
93
|
+
- A saved API key is used only with the provider it was saved for, and changing provider
|
|
94
|
+
with `ucx llm use` keeps it.
|
|
95
|
+
|
|
96
|
+
### Compatibility
|
|
97
|
+
|
|
98
|
+
- `settings.json` written by 0.2.2 can carry `llm_api_key_provider`, which 0.2.1
|
|
99
|
+
ignores: 0.2.1 applies a saved API key to whichever provider is saved, and a Settings
|
|
100
|
+
save in 0.2.1 removes the field. 0.2.1's terminal commands also do not read the saved
|
|
101
|
+
default.
|
|
102
|
+
- A conversation that contains a turn refused because its model cannot use tools records
|
|
103
|
+
a reason 0.2.1 does not know, so 0.2.1 may not open it.
|
|
104
|
+
|
|
16
105
|
## [0.2.1] - 2026-09-24
|
|
17
106
|
|
|
18
107
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: uclone-x
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: Next-Generation Event-Driven AI Agent Core & High-Performance Collaboration Framework
|
|
5
5
|
Author-email: Kenny Lim <kennylim@uclone.net>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -21,6 +21,7 @@ Requires-Dist: pydantic-settings>=2.2.0
|
|
|
21
21
|
Requires-Dist: pydantic>=2.7.0
|
|
22
22
|
Requires-Dist: pyyaml>=6.0.1
|
|
23
23
|
Provides-Extra: all
|
|
24
|
+
Requires-Dist: accelerate>=0.31.0; extra == 'all'
|
|
24
25
|
Requires-Dist: anthropic>=0.25.0; extra == 'all'
|
|
25
26
|
Requires-Dist: diffusers>=0.31.0; extra == 'all'
|
|
26
27
|
Requires-Dist: fastapi>=0.110.0; extra == 'all'
|
|
@@ -71,6 +72,7 @@ Requires-Dist: anthropic>=0.25.0; extra == 'llm'
|
|
|
71
72
|
Requires-Dist: google-genai>=0.1.0; extra == 'llm'
|
|
72
73
|
Requires-Dist: openai>=1.25.0; extra == 'llm'
|
|
73
74
|
Provides-Extra: media
|
|
75
|
+
Requires-Dist: accelerate>=0.31.0; extra == 'media'
|
|
74
76
|
Requires-Dist: diffusers>=0.31.0; extra == 'media'
|
|
75
77
|
Requires-Dist: pillow>=10.0.0; extra == 'media'
|
|
76
78
|
Requires-Dist: torch>=2.2.0; extra == 'media'
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "uclone-x"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.2"
|
|
8
8
|
description = "Next-Generation Event-Driven AI Agent Core & High-Performance Collaboration Framework"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -101,8 +101,14 @@ ontology = [
|
|
|
101
101
|
# `ucx media status` reporting ready for an engine that would fail on `import torch`.
|
|
102
102
|
# The floors here are the same ones `IN_PROCESS_REQUIREMENTS` checks at runtime, so a
|
|
103
103
|
# beginner who installs this extra and a beginner who follows the hint end up equivalent.
|
|
104
|
+
#
|
|
105
|
+
# `accelerate` is what `enable_model_cpu_offload` needs. It is not a runtime requirement
|
|
106
|
+
# (MPS and CPU never offload, and a CUDA card with room loads the whole pipeline), so it is
|
|
107
|
+
# not in `IN_PROCESS_REQUIREMENTS`; it is here so an NVIDIA card whose memory is shared
|
|
108
|
+
# with a resident Ollama model can still run SDXL instead of running out of memory.
|
|
104
109
|
media = [
|
|
105
110
|
"pillow>=10.0.0",
|
|
111
|
+
"accelerate>=0.31.0",
|
|
106
112
|
"diffusers>=0.31.0",
|
|
107
113
|
"torch>=2.2.0",
|
|
108
114
|
"transformers>=4.40.0",
|
|
@@ -13,7 +13,7 @@ from dataclasses import dataclass, field
|
|
|
13
13
|
from datetime import UTC, datetime
|
|
14
14
|
from enum import Enum
|
|
15
15
|
from pathlib import Path
|
|
16
|
-
from typing import Any, Final, Literal, cast
|
|
16
|
+
from typing import Any, Final, Literal, TypeVar, cast
|
|
17
17
|
|
|
18
18
|
from uclone_x.agent.grounding import describe, unsupported_specifics
|
|
19
19
|
from uclone_x.agent.hooks import (
|
|
@@ -101,6 +101,7 @@ from uclone_x.errors import (
|
|
|
101
101
|
LLMConnectorNotConfiguredError,
|
|
102
102
|
LLMStreamInterruptedError,
|
|
103
103
|
LLMTimeoutError,
|
|
104
|
+
ModelLacksToolSupportError,
|
|
104
105
|
PathTraversalError,
|
|
105
106
|
SessionMutationDuringTurnError,
|
|
106
107
|
SessionStoreNotConfiguredError,
|
|
@@ -246,24 +247,61 @@ class _StreamProgress:
|
|
|
246
247
|
return "".join(self.thinking_chunks) if self.thinking_chunks else None
|
|
247
248
|
|
|
248
249
|
|
|
249
|
-
|
|
250
|
-
|
|
250
|
+
_ChainedError = TypeVar("_ChainedError", bound=BaseException)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
def _in_cause_chain(exc: BaseException, kind: type[_ChainedError]) -> _ChainedError | None:
|
|
254
|
+
"""The first exception of type `kind` in `exc`'s `__cause__` chain, `exc` included.
|
|
251
255
|
|
|
252
256
|
The chain is walked rather than the exception tested directly, because the streaming
|
|
253
257
|
path does not deliver the connector's error itself: `_invoke_model` raises
|
|
254
258
|
`LLMStreamInterruptedError` with the provider's error chained as `__cause__`. Testing
|
|
255
|
-
only the outermost type would report a cut-off stream as an ordinary failure
|
|
256
|
-
the distinction this function exists to make.
|
|
259
|
+
only the outermost type would report a cut-off stream as an ordinary failure.
|
|
257
260
|
"""
|
|
258
261
|
seen = exc
|
|
259
262
|
for _ in range(_MAX_CAUSE_DEPTH):
|
|
260
|
-
if isinstance(seen,
|
|
261
|
-
return
|
|
263
|
+
if isinstance(seen, kind):
|
|
264
|
+
return seen
|
|
262
265
|
cause = seen.__cause__
|
|
263
266
|
if cause is None:
|
|
264
|
-
return
|
|
267
|
+
return None
|
|
265
268
|
seen = cause
|
|
266
|
-
return
|
|
269
|
+
return None
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def _is_provider_timeout(exc: BaseException) -> bool:
|
|
273
|
+
"""Whether a turn failed because a provider ran past the ceiling the caller set."""
|
|
274
|
+
return _in_cause_chain(exc, LLMTimeoutError) is not None
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def _model_lacking_tools(exc: BaseException) -> ModelLacksToolSupportError | None:
|
|
278
|
+
"""The refusal of a model that cannot take tools, if that is why a turn failed.
|
|
279
|
+
|
|
280
|
+
The error the turn reports when it is found: its message is written for the user,
|
|
281
|
+
while the `LLMStreamInterruptedError` wrapping it names classes and a status line.
|
|
282
|
+
"""
|
|
283
|
+
return _in_cause_chain(exc, ModelLacksToolSupportError)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def _turn_failure(
|
|
287
|
+
exc: BaseException, stop_reason: TurnStopReason, agent_id: str
|
|
288
|
+
) -> tuple[TurnStopReason, str]:
|
|
289
|
+
"""The stop reason and `error` a turn that raised `exc` ends with, logged as it deserves.
|
|
290
|
+
|
|
291
|
+
A function of its own because `execute_turn` is at the edge of what the type checker
|
|
292
|
+
can analyse, and each branch added inline pushes it over.
|
|
293
|
+
"""
|
|
294
|
+
if _is_provider_timeout(exc):
|
|
295
|
+
stop_reason = "provider_timeout"
|
|
296
|
+
lacking_tools = _model_lacking_tools(exc)
|
|
297
|
+
if lacking_tools is None:
|
|
298
|
+
logger.exception("Error executing turn for agent %s", agent_id)
|
|
299
|
+
return stop_reason, str(exc)
|
|
300
|
+
# A choice of model, not a fault in the code. With no handler set up, Python prints
|
|
301
|
+
# WARNING and above -- traceback included -- on the user's terminal, beside the plain
|
|
302
|
+
# sentence the CLI already shows.
|
|
303
|
+
logger.info("Turn for agent %s refused: %s", agent_id, lacking_tools)
|
|
304
|
+
return "model_without_tools", str(lacking_tools)
|
|
267
305
|
|
|
268
306
|
|
|
269
307
|
class _AnchorWriter(Enum):
|
|
@@ -4530,9 +4568,7 @@ class BaseAgent(BaseAgentProtocol):
|
|
|
4530
4568
|
# Named before anything else reads it. `error` carries the same fact as
|
|
4531
4569
|
# prose, and prose is not something a caller can branch on without
|
|
4532
4570
|
# guessing at a provider's wording (#1277).
|
|
4533
|
-
|
|
4534
|
-
stop_reason = "provider_timeout"
|
|
4535
|
-
logger.exception("Error executing turn for agent %s", self.agent_id)
|
|
4571
|
+
stop_reason, failure = _turn_failure(exc, stop_reason, self.agent_id)
|
|
4536
4572
|
req_model = self._config.llm_config.model_name
|
|
4537
4573
|
|
|
4538
4574
|
# Execute ON_ERROR hook
|
|
@@ -4626,7 +4662,7 @@ class BaseAgent(BaseAgentProtocol):
|
|
|
4626
4662
|
tool_executions=tuple(tool_executions), # ran before the failure
|
|
4627
4663
|
tool_executions_complete=not tools_unreported, # a failure mid-step
|
|
4628
4664
|
is_completed=False,
|
|
4629
|
-
error=
|
|
4665
|
+
error=failure,
|
|
4630
4666
|
stop_reason=stop_reason,
|
|
4631
4667
|
correlation_id=correlation_id,
|
|
4632
4668
|
provenance=failover_provenance,
|
|
@@ -116,6 +116,7 @@ BASE_PERSONA_TOOLS: tuple[str, ...] = (
|
|
|
116
116
|
"file_search",
|
|
117
117
|
"directory_list",
|
|
118
118
|
"tool_result_read",
|
|
119
|
+
"load_skill",
|
|
119
120
|
)
|
|
120
121
|
|
|
121
122
|
|
|
@@ -440,6 +441,7 @@ TurnStopReason = Literal[
|
|
|
440
441
|
"step_results_over_window",
|
|
441
442
|
"budget_exceeded",
|
|
442
443
|
"provider_timeout",
|
|
444
|
+
"model_without_tools",
|
|
443
445
|
"cancelled",
|
|
444
446
|
]
|
|
445
447
|
"""How a turn's step run ended: the vocabulary of `TURN_END.stop_reason` and `TurnResult`.
|
|
@@ -458,6 +460,11 @@ connector's ceiling and a probe whose host was not running both reached the repo
|
|
|
458
460
|
distribution. A consumer that must not count a cut-off turn as the model's failure needs
|
|
459
461
|
a field it can test, not a phrase it can grep. It is not a refusal -- `turn_refusal`
|
|
460
462
|
leaves it `None` -- because a retry at a longer ceiling is exactly the right response.
|
|
463
|
+
|
|
464
|
+
`model_without_tools` is a provider's refusal of the chosen model because it cannot take
|
|
465
|
+
tool definitions, which every clone turn sends. Its `error` is a plain sentence naming the
|
|
466
|
+
model and the remedy, written to be shown as is. It is a refusal: a retry on the same model
|
|
467
|
+
is refused the same way, so `turn_refusal` maps it to `RoomTurnRefusal.MODEL_WITHOUT_TOOLS`.
|
|
461
468
|
"""
|
|
462
469
|
|
|
463
470
|
|
|
@@ -203,11 +203,26 @@ HERMES_STEERABILITY_POLICY: Final[str] = (
|
|
|
203
203
|
"and respond naturally in the language in which the user communicates."
|
|
204
204
|
)
|
|
205
205
|
|
|
206
|
+
QWEN_STEERABILITY_POLICY: Final[str] = (
|
|
207
|
+
"For Qwen runtime models, accuracy constraints govern precision and tool routing; on their own they are not a reason "
|
|
208
|
+
"to decline an otherwise reasonable request. "
|
|
209
|
+
"Tool Calling Discipline: Do not invoke file modification or state-changing action tools for conversational advice, brainstorming, or general reasoning. "
|
|
210
|
+
"Actively use read-only search and inspection tools when grounded evidence from the workspace or web is required, and invoke modification tools only when explicitly commanded. "
|
|
211
|
+
"Language Matching: Always respond fluently and consistently in the exact language used by the user's prompt. "
|
|
212
|
+
"Respect User Agency: Treat the user as an autonomous decision-maker, and follow their "
|
|
213
|
+
"instructions and style requests without imposing personal or ideological judgments. "
|
|
214
|
+
"Substantive Answers: Provide detailed, substantive answers that directly address the prompt. "
|
|
215
|
+
"No Moralizing: Do not add patronizing commentary, moral lectures, unsolicited caveats, or boilerplate disclaimers. "
|
|
216
|
+
"Tone Adaptation: Match the tone, intensity, and creative or stylistic direction requested by the user, "
|
|
217
|
+
"and respond naturally in the language in which the user communicates."
|
|
218
|
+
)
|
|
219
|
+
|
|
206
220
|
#: Families whose framing departs from `STEERABILITY_POLICY`. A family absent from this
|
|
207
221
|
#: mapping gets the default — that is a declared fallback, not a silent one, and
|
|
208
222
|
#: `steerability_policy_for` is the single place it is resolved.
|
|
209
223
|
_FAMILY_STEERABILITY: Final[Mapping[ModelFamily, str]] = {
|
|
210
224
|
ModelFamily.HERMES: HERMES_STEERABILITY_POLICY,
|
|
225
|
+
ModelFamily.QWEN: QWEN_STEERABILITY_POLICY,
|
|
211
226
|
}
|
|
212
227
|
|
|
213
228
|
|
|
@@ -306,6 +321,7 @@ __all__ = [
|
|
|
306
321
|
"HONEST_REPORTING",
|
|
307
322
|
"IDENTITY_GROUNDING",
|
|
308
323
|
"IMAGE_GENERATION",
|
|
324
|
+
"QWEN_STEERABILITY_POLICY",
|
|
309
325
|
"STEERABILITY_POLICY",
|
|
310
326
|
"TASK_CAPABILITIES",
|
|
311
327
|
"CapabilityFragment",
|
|
@@ -82,11 +82,15 @@ def start_a2a_server(
|
|
|
82
82
|
from uclone_x.agent.composition import HostDependencies, compose_agent
|
|
83
83
|
from uclone_x.agent.session import SessionStore
|
|
84
84
|
from uclone_x.cli.agent_memory import memory_for_agent_id
|
|
85
|
-
from uclone_x.llm.connectors.factory import create_llm_connector
|
|
85
|
+
from uclone_x.llm.connectors.factory import create_llm_connector, saved_choice_notice
|
|
86
86
|
from uclone_x.shells.a2a_server import A2AServer
|
|
87
87
|
from uclone_x.telemetry import TelemetryTracer
|
|
88
88
|
from uclone_x.tools.registry import create_default_registry
|
|
89
89
|
|
|
90
|
+
saved_notice = saved_choice_notice()
|
|
91
|
+
if saved_notice is not None:
|
|
92
|
+
# stderr, as ACP does: what the server says to its caller stays on its own channel.
|
|
93
|
+
Console(stderr=True).print(saved_notice, markup=False, highlight=False)
|
|
90
94
|
llm = create_llm_connector(fallback_to_mock=True)
|
|
91
95
|
memory = memory_for_agent_id(agent_config.agent_id)
|
|
92
96
|
|
|
@@ -18,7 +18,7 @@ from uclone_x.agent.persona_store import DEFAULT_PERSONA_NAME
|
|
|
18
18
|
from uclone_x.agent.session import SessionStore
|
|
19
19
|
from uclone_x.cli.agent_memory import memory_for_agent_id
|
|
20
20
|
from uclone_x.engine.event_bus import EventBus
|
|
21
|
-
from uclone_x.llm.connectors.factory import create_llm_connector
|
|
21
|
+
from uclone_x.llm.connectors.factory import create_llm_connector, saved_choice_notice
|
|
22
22
|
from uclone_x.shells.acp import ACPServer
|
|
23
23
|
from uclone_x.shells.acp.server import SessionAgentFactory
|
|
24
24
|
from uclone_x.telemetry import TelemetryTracer
|
|
@@ -30,6 +30,8 @@ acp_app = typer.Typer(
|
|
|
30
30
|
no_args_is_help=True,
|
|
31
31
|
)
|
|
32
32
|
console = Console()
|
|
33
|
+
# ACP speaks over stdout, so anything said to the person goes to stderr.
|
|
34
|
+
err_console = Console(stderr=True)
|
|
33
35
|
|
|
34
36
|
|
|
35
37
|
def session_agent_factory(config: AgentConfig, host: HostDependencies) -> SessionAgentFactory:
|
|
@@ -80,6 +82,9 @@ def start_acp_server(agent_id: str | None = None, persona: str = DEFAULT_PERSONA
|
|
|
80
82
|
agent_config = agent_config_for_persona(persona_def, agent_id=agent_id)
|
|
81
83
|
|
|
82
84
|
bus = EventBus()
|
|
85
|
+
saved_notice = saved_choice_notice()
|
|
86
|
+
if saved_notice is not None:
|
|
87
|
+
err_console.print(saved_notice, markup=False, highlight=False)
|
|
83
88
|
llm = create_llm_connector(fallback_to_mock=True)
|
|
84
89
|
store = SessionStore()
|
|
85
90
|
tracer = TelemetryTracer()
|
|
@@ -370,6 +370,24 @@ def ollama_installer_env() -> dict[str, str]:
|
|
|
370
370
|
return env
|
|
371
371
|
|
|
372
372
|
|
|
373
|
+
#: How many times the official Ollama installer is run before giving up. Its download is
|
|
374
|
+
#: a single ~1 GB archive with no resume, and a reset connection at 95% (measured on a Mac
|
|
375
|
+
#: on a flaky network: `curl: (56) Recv failure: Connection reset by peer`) otherwise
|
|
376
|
+
#: ends the whole setup at "not runnable".
|
|
377
|
+
OLLAMA_INSTALL_ATTEMPTS: Final = 3
|
|
378
|
+
#: Seconds to wait before running the installer again, multiplied by the attempt number.
|
|
379
|
+
OLLAMA_INSTALL_RETRY_DELAY_S: float = 5.0
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def _ollama_runnable() -> tuple[str, str] | None:
|
|
383
|
+
"""``(binary, version)`` when an ollama can be found and run, else ``None``."""
|
|
384
|
+
binary = ollama_binary()
|
|
385
|
+
version = ollama_version(binary)
|
|
386
|
+
if binary is None or version is None:
|
|
387
|
+
return None
|
|
388
|
+
return binary, version
|
|
389
|
+
|
|
390
|
+
|
|
373
391
|
def install_ollama_platform() -> bool:
|
|
374
392
|
"""Install Ollama, then verify by running it — never by trusting the exit code.
|
|
375
393
|
|
|
@@ -380,30 +398,50 @@ def install_ollama_platform() -> bool:
|
|
|
380
398
|
on the measured Mac mini (its `sudo ln` fails) while leaving a perfectly usable app
|
|
381
399
|
behind, so exit code and reality disagree in both directions and only one of them can
|
|
382
400
|
be measured.
|
|
401
|
+
|
|
402
|
+
The official installer is run up to ``OLLAMA_INSTALL_ATTEMPTS`` times while the binary
|
|
403
|
+
is still not runnable afterwards, because the usual cause is a download cut off part
|
|
404
|
+
way. A run that hits the 15-minute timeout is not repeated: another 15 minutes on the
|
|
405
|
+
same connection is not a remedy, and the person has already waited.
|
|
383
406
|
"""
|
|
384
407
|
os_type = platform.system()
|
|
385
408
|
if os_type == "Darwin" and shutil.which("brew"):
|
|
386
409
|
console.print("[bold cyan]📦 Installing Ollama via Homebrew...[/bold cyan]")
|
|
387
410
|
subprocess.run(["brew", "install", "ollama"], check=False, stdin=subprocess.DEVNULL)
|
|
411
|
+
found = _ollama_runnable()
|
|
388
412
|
elif os_type in ("Darwin", "Linux"):
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
413
|
+
found = None
|
|
414
|
+
for attempt in range(1, OLLAMA_INSTALL_ATTEMPTS + 1):
|
|
415
|
+
if attempt == 1:
|
|
416
|
+
console.print(
|
|
417
|
+
"[bold cyan]📦 Installing Ollama via official install script...[/bold cyan]"
|
|
418
|
+
)
|
|
419
|
+
try:
|
|
420
|
+
subprocess.run(
|
|
421
|
+
"curl -fsSL https://ollama.com/install.sh | sh",
|
|
422
|
+
shell=True,
|
|
423
|
+
check=False,
|
|
424
|
+
stdin=subprocess.DEVNULL,
|
|
425
|
+
env=ollama_installer_env(),
|
|
426
|
+
timeout=900,
|
|
427
|
+
)
|
|
428
|
+
except subprocess.TimeoutExpired:
|
|
429
|
+
console.print(
|
|
430
|
+
"[bold red]✖ The Ollama installer did not finish in 15 minutes.[/bold red]"
|
|
431
|
+
)
|
|
432
|
+
return False
|
|
433
|
+
except Exception as exc:
|
|
434
|
+
console.print(f"[bold red]✖ Could not run the Ollama installer: {exc}[/bold red]")
|
|
435
|
+
return False
|
|
436
|
+
found = _ollama_runnable()
|
|
437
|
+
if found is not None or attempt == OLLAMA_INSTALL_ATTEMPTS:
|
|
438
|
+
break
|
|
400
439
|
console.print(
|
|
401
|
-
"[
|
|
440
|
+
f"[yellow]The Ollama download did not finish, which usually means the "
|
|
441
|
+
f"connection dropped. Trying again ({attempt + 1} of "
|
|
442
|
+
f"{OLLAMA_INSTALL_ATTEMPTS})...[/yellow]"
|
|
402
443
|
)
|
|
403
|
-
|
|
404
|
-
except Exception as exc:
|
|
405
|
-
console.print(f"[bold red]✖ Could not run the Ollama installer: {exc}[/bold red]")
|
|
406
|
-
return False
|
|
444
|
+
time.sleep(OLLAMA_INSTALL_RETRY_DELAY_S * attempt)
|
|
407
445
|
else:
|
|
408
446
|
console.print(
|
|
409
447
|
f"[bold red]✖ No automated Ollama install for {escape(os_type)}. "
|
|
@@ -411,14 +449,13 @@ def install_ollama_platform() -> bool:
|
|
|
411
449
|
)
|
|
412
450
|
return False
|
|
413
451
|
|
|
414
|
-
|
|
415
|
-
version = ollama_version(binary)
|
|
416
|
-
if binary is None or version is None:
|
|
452
|
+
if found is None:
|
|
417
453
|
console.print(
|
|
418
454
|
"[bold red]✖ Ollama is still not runnable after the install.[/bold red] "
|
|
419
455
|
"Looked on PATH and at " + escape(", ".join(OLLAMA_APP_BINARIES)) + "."
|
|
420
456
|
)
|
|
421
457
|
return False
|
|
458
|
+
binary, version = found
|
|
422
459
|
console.print(f"[bold green]✔ Ollama ready:[/bold green] {escape(binary)} ({escape(version)})")
|
|
423
460
|
return True
|
|
424
461
|
|
|
@@ -675,9 +712,9 @@ def diffusers_install_command() -> list[str] | None:
|
|
|
675
712
|
that stops at `diffusers` leaves the engine unable to generate (#1095).
|
|
676
713
|
"""
|
|
677
714
|
from uclone_x.core.environment_install import installer_command
|
|
678
|
-
from uclone_x.tools.builtin.image import
|
|
715
|
+
from uclone_x.tools.builtin.image import IN_PROCESS_INSTALL_REQUIREMENTS
|
|
679
716
|
|
|
680
|
-
packages = [requirement for _, requirement, _ in
|
|
717
|
+
packages = [requirement for _, requirement, _ in IN_PROCESS_INSTALL_REQUIREMENTS]
|
|
681
718
|
return installer_command(*packages)
|
|
682
719
|
|
|
683
720
|
|
|
@@ -1223,6 +1260,76 @@ class LocalSetupResult(NamedTuple):
|
|
|
1223
1260
|
return [part.summary_line() for part in (self.llm, self.image) if part is not None]
|
|
1224
1261
|
|
|
1225
1262
|
|
|
1263
|
+
def _same_address(saved: str | None, prepared: str | None) -> bool:
|
|
1264
|
+
"""Whether a saved Ollama address reaches the daemon setup prepared.
|
|
1265
|
+
|
|
1266
|
+
No saved address means the connector resolves the usual one, which is the one setup
|
|
1267
|
+
checked, so it counts as the same.
|
|
1268
|
+
"""
|
|
1269
|
+
if saved is None or prepared is None:
|
|
1270
|
+
return True
|
|
1271
|
+
return saved.rstrip("/") == prepared.rstrip("/")
|
|
1272
|
+
|
|
1273
|
+
|
|
1274
|
+
def remember_local_llm(result: LlmSetup) -> None:
|
|
1275
|
+
"""Save the model setup just made ready as the default, unless one is already saved.
|
|
1276
|
+
|
|
1277
|
+
Without this, `ucx install --yes` printed `setup llm: ready model=...` and the next
|
|
1278
|
+
`ucx run` refused with "No LLM provider is configured": nothing setup did was anywhere
|
|
1279
|
+
the terminal commands looked. The choice goes to the file the dashboard's Settings
|
|
1280
|
+
already keep, so there is one saved choice for every head. A choice already saved --
|
|
1281
|
+
by the person in Settings, or by an earlier install -- is kept; setup only fills in
|
|
1282
|
+
what is missing.
|
|
1283
|
+
|
|
1284
|
+
What is printed is read back from the file after the write, not assumed from it: when
|
|
1285
|
+
the saved default is not what setup just prepared, the person is told both, and how to
|
|
1286
|
+
switch. A failure to save is reported and does not fail the setup: the model is still
|
|
1287
|
+
ready, and `--provider`/`--model` or the dashboard still reach it.
|
|
1288
|
+
"""
|
|
1289
|
+
from uclone_x.llm.connectors.saved_choice import read_saved_choice, remember_choice_if_unset
|
|
1290
|
+
|
|
1291
|
+
model = escape(result.model)
|
|
1292
|
+
try:
|
|
1293
|
+
written, _before = remember_choice_if_unset(
|
|
1294
|
+
provider="ollama", model=result.model, base_url=result.endpoint
|
|
1295
|
+
)
|
|
1296
|
+
saved = read_saved_choice()
|
|
1297
|
+
except (OSError, ValueError):
|
|
1298
|
+
console.print(
|
|
1299
|
+
"[yellow]Could not save this model as the default, so `ucx run` will not pick "
|
|
1300
|
+
"it up on its own. Choose it in the dashboard's Settings, or pass "
|
|
1301
|
+
f"--provider ollama --model {model}.[/yellow]"
|
|
1302
|
+
)
|
|
1303
|
+
return
|
|
1304
|
+
if saved is None:
|
|
1305
|
+
return
|
|
1306
|
+
switch = (
|
|
1307
|
+
f"To use {model} instead, run `ucx llm use {model}` or choose it in the "
|
|
1308
|
+
"dashboard's Settings."
|
|
1309
|
+
)
|
|
1310
|
+
if saved.provider != "ollama" or saved.model not in (None, result.model):
|
|
1311
|
+
what = escape(saved.model or saved.provider)
|
|
1312
|
+
if saved.provider != "ollama":
|
|
1313
|
+
what = f"{what} ({escape(saved.provider)})"
|
|
1314
|
+
console.print(
|
|
1315
|
+
f"[yellow]{what} is already saved as the default model, so it stays the "
|
|
1316
|
+
f"default rather than {model}, which setup just prepared. {switch}[/yellow]"
|
|
1317
|
+
)
|
|
1318
|
+
elif not _same_address(saved.base_url, result.endpoint):
|
|
1319
|
+
console.print(
|
|
1320
|
+
f"[yellow]The saved default asks for {model} at {escape(saved.base_url or '')}, "
|
|
1321
|
+
f"but setup prepared it at {escape(result.endpoint)}. To use this one, run "
|
|
1322
|
+
f"`ucx llm use {model} --base-url {escape(result.endpoint)}`.[/yellow]"
|
|
1323
|
+
)
|
|
1324
|
+
elif written:
|
|
1325
|
+
console.print(
|
|
1326
|
+
f"[green]✔ Saved {model} as the default model for `ucx run`, "
|
|
1327
|
+
"rooms and the dashboard.[/green]"
|
|
1328
|
+
)
|
|
1329
|
+
else:
|
|
1330
|
+
console.print(f"[dim]Kept {model}, the model already saved as the default.[/dim]")
|
|
1331
|
+
|
|
1332
|
+
|
|
1226
1333
|
def run_local_setup(
|
|
1227
1334
|
*,
|
|
1228
1335
|
llm: bool = True,
|
|
@@ -1238,8 +1345,11 @@ def run_local_setup(
|
|
|
1238
1345
|
copy of this sequence inline; two copies of "prepare the local models" is how one of
|
|
1239
1346
|
them silently stops matching the other.
|
|
1240
1347
|
"""
|
|
1348
|
+
llm_result = setup_local_llm(interactive=interactive, assume_yes=assume_yes) if llm else None
|
|
1349
|
+
if llm_result is not None and llm_result.ready:
|
|
1350
|
+
remember_local_llm(llm_result)
|
|
1241
1351
|
return LocalSetupResult(
|
|
1242
|
-
llm=
|
|
1352
|
+
llm=llm_result,
|
|
1243
1353
|
image=(
|
|
1244
1354
|
setup_local_image(
|
|
1245
1355
|
interactive=interactive,
|
|
@@ -124,7 +124,9 @@ def eval_run(
|
|
|
124
124
|
] = "all",
|
|
125
125
|
model: Annotated[
|
|
126
126
|
str | None,
|
|
127
|
-
typer.Option(
|
|
127
|
+
typer.Option(
|
|
128
|
+
"--model", "-m", help="Target model identifier override (e.g. qwen3:8b, hermes3:8b)"
|
|
129
|
+
),
|
|
128
130
|
] = None,
|
|
129
131
|
provider: Annotated[
|
|
130
132
|
str | None,
|