multacd 2.0.0b4__tar.gz → 2.0.0b5__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.
- {multacd-2.0.0b4 → multacd-2.0.0b5}/.github/workflows/release.yml +1 -1
- {multacd-2.0.0b4 → multacd-2.0.0b5}/CHANGELOG.md +7 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/PKG-INFO +1 -1
- {multacd-2.0.0b4 → multacd-2.0.0b5}/core/providers/openai_compat.py +24 -1
- {multacd-2.0.0b4 → multacd-2.0.0b5}/core/version.py +1 -1
- {multacd-2.0.0b4 → multacd-2.0.0b5}/memory/context.py +9 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/pyproject.toml +1 -1
- {multacd-2.0.0b4 → multacd-2.0.0b5}/scripts/install.sh +1 -1
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tests/integration/test_agent_loop.py +4 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/.github/pull_request_template.md +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/.github/workflows/test.yml +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/.gitignore +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/CONTRIBUTING.md +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/DESIGN.md +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/LICENSE +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/README.md +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/briefing/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/briefing/generator.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/briefing/privacy.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/briefing/sources/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/briefing/sources/git_source.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/briefing/sources/news_source.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/briefing/sources/todo_source.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/core/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/core/agent_loop.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/core/clipboard.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/core/codebase.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/core/config.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/core/llm_client.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/core/mode_manager.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/core/permissions.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/core/plugin_loader.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/core/prompt_composer.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/core/providers/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/core/providers/__main__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/core/providers/anthropic.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/core/research/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/core/research/bus.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/core/research/orchestrator.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/core/research/query_generator.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/core/updater.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/daemon/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/daemon/ipc.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/daemon/process.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/docs/configuration.md +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/docs/index.md +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/docs/install.md +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/docs/mkdocs.yml +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/docs/modes/coding.md +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/docs/modes/personal.md +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/docs/modes/research.md +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/docs/plugins.md +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/docs/quickstart.md +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/main.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/memory/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/memory/store.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/multacd.spec +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/plan/PLAN-phase1.md +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/plan/PLAN-phase2.md +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/plugins/README.md +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/plugins/example_plugin.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/requirements.txt +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/roadmap/ROADMAP.md +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/scheduler/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/scheduler/engine.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/scheduler/jobs/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/scheduler/jobs/briefing_job.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/scheduler/jobs/research_job.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/scripts/install.ps1 +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/search_providers/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/search_providers/__main__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/search_providers/base.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/search_providers/brave.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/search_providers/duckduckgo.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/search_providers/exa.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/search_providers/serpapi.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/search_providers/tavily.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/soul.md +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tests/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tests/conftest.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tests/integration/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tests/test_selftests.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tests/unit/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tests/unit/test_binary.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tests/unit/test_config.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tests/unit/test_docs.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tests/unit/test_entrypoint.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tests/unit/test_github.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tests/unit/test_installer.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tests/unit/test_permissions.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tests/unit/test_plugin_loader.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tests/unit/test_privacy_filter.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tests/unit/test_search_providers/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tests/unit/test_search_providers/test_tavily.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tests/unit/test_tools/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tests/unit/test_tools/test_bash.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tests/unit/test_tools/test_read_file.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tests/unit/test_tools/test_web_scrape.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tests/unit/test_tools/test_write_file.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tests/unit/test_updater.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tg/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tg/access_control.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tg/agent.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tg/bot.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tg/file_handler.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tg/formatter.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tg/handlers.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/agent/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/agent/ask.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/agent/skill.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/agent/task.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/agent/todo_write.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/code/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/code/lint_python.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/code/run_python.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/code/run_tests.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/codebase/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/codebase/scan_codebase.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/common.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/filesystem/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/filesystem/apply_patch.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/filesystem/delete_file.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/filesystem/edit_file.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/filesystem/glob.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/filesystem/grep.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/filesystem/list_dir.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/filesystem/move_file.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/filesystem/multi_edit.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/filesystem/read_file.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/filesystem/read_many_files.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/filesystem/write_file.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/git/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/git/_helper.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/git/git_add.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/git/git_branch.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/git/git_checkout.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/git/git_commit.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/git/git_diff.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/git/git_log.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/git/git_merge.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/git/git_pull.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/git/git_push.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/git/git_status.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/memory/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/memory/forget.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/memory/recall.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/memory/remember.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/personal/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/personal/cancel_job.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/personal/daemon_status.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/personal/generate_briefing.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/personal/get_jobs.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/personal/schedule_job.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/personal/send_telegram.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/personal/user_manager.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/registry.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/research/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/research/deep_research.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/research/export_research.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/research/quick_research.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/research/web_scrape.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/research/web_search.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/shell/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/shell/bash.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/streaming.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/web/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tools/web/web_fetch.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tui/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tui/app.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tui/icons.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tui/screens/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tui/screens/main_screen.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tui/screens/setup_wizard.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tui/themes/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tui/themes/__main__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tui/tokens.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tui/widgets/__init__.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tui/widgets/chat_panel.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tui/widgets/confirm_dialog.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tui/widgets/diff_viewer.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tui/widgets/file_tree.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tui/widgets/info_panel.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tui/widgets/input_bar.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tui/widgets/model_selector.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tui/widgets/permission_popup.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tui/widgets/slash_palette.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tui/widgets/sources_panel.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tui/widgets/status_bar.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tui/widgets/thinking_bar.py +0 -0
- {multacd-2.0.0b4 → multacd-2.0.0b5}/tui/widgets/tool_activity.py +0 -0
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
Format: Keep a Changelog. Versi: Semantic Versioning.
|
|
4
4
|
|
|
5
|
+
## [2.0.0b5] - 2026-09-12
|
|
6
|
+
|
|
7
|
+
Beta v2 kelima — fix chat 2-turn 400 (#25): `add_assistant_tool_calls`
|
|
8
|
+
omit key `tool_calls` saat tanpa tool + `_sanitize_messages` safety net
|
|
9
|
+
di openai_compat. Belajar dari opencode `transform.ts` (part kosong
|
|
10
|
+
tidak pernah dikirim). Install: `pip install "multacd==2.0.0b5"`.
|
|
11
|
+
|
|
5
12
|
## [2.0.0b4] - 2026-09-12
|
|
6
13
|
|
|
7
14
|
Beta v2 keempat — Termux harusnya tembus sekarang (#24): `pydantic`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: multacd
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.0b5
|
|
4
4
|
Summary: Agentic TUI for coding, research, and personal automation
|
|
5
5
|
Project-URL: Homepage, https://github.com/prototypeall850-creator/multacd
|
|
6
6
|
Project-URL: Issues, https://github.com/prototypeall850-creator/multacd/issues
|
|
@@ -136,6 +136,29 @@ def map_http_error(status: int, body: str, model: str) -> ProviderError:
|
|
|
136
136
|
return ProviderError(f"Provider error HTTP {status}. {snippet}".strip())
|
|
137
137
|
|
|
138
138
|
|
|
139
|
+
def _sanitize_messages(messages: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
140
|
+
"""Bersihkan messages sebelum kirim (ala opencode transform.ts).
|
|
141
|
+
|
|
142
|
+
- `tool_calls: []` / None → key di-drop (provider strict 400 kalau
|
|
143
|
+
array kosong ikut terkirim — issue #25).
|
|
144
|
+
- `content: None` tanpa tool_calls → `""` (beberapa gateway nolak null).
|
|
145
|
+
- `content: None` + tool_calls non-kosong → dibiarkan (valid OpenAI).
|
|
146
|
+
Pure function — input tidak dimutasi.
|
|
147
|
+
"""
|
|
148
|
+
clean: list[dict[str, Any]] = []
|
|
149
|
+
for m in messages:
|
|
150
|
+
if not isinstance(m, dict):
|
|
151
|
+
continue
|
|
152
|
+
msg = dict(m)
|
|
153
|
+
tcs = msg.get("tool_calls")
|
|
154
|
+
if not tcs:
|
|
155
|
+
msg.pop("tool_calls", None)
|
|
156
|
+
if msg.get("content") is None and not msg.get("tool_calls"):
|
|
157
|
+
msg["content"] = ""
|
|
158
|
+
clean.append(msg)
|
|
159
|
+
return clean
|
|
160
|
+
|
|
161
|
+
|
|
139
162
|
async def stream_chat(spec: ProviderSpec, api_key: str,
|
|
140
163
|
messages: list[dict[str, Any]],
|
|
141
164
|
tools: list[dict[str, Any]] | None,
|
|
@@ -146,7 +169,7 @@ async def stream_chat(spec: ProviderSpec, api_key: str,
|
|
|
146
169
|
"""Kirim chat completions streaming. `client` injeksi buat test."""
|
|
147
170
|
body: dict[str, Any] = {
|
|
148
171
|
"model": spec.model,
|
|
149
|
-
"messages": messages,
|
|
172
|
+
"messages": _sanitize_messages(messages),
|
|
150
173
|
"stream": True,
|
|
151
174
|
"stream_options": {"include_usage": True},
|
|
152
175
|
"max_tokens": max_tokens,
|
|
@@ -8,7 +8,7 @@ Saat bump versi (Step 10 → 1.0.0): ubah pyproject + FALLBACK di sini.
|
|
|
8
8
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
|
-
FALLBACK = "2.0.
|
|
11
|
+
FALLBACK = "2.0.0b5" # beta v2; samakan dengan pyproject.toml
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
def get_version() -> str:
|
|
@@ -56,9 +56,18 @@ class ConversationContext:
|
|
|
56
56
|
(mis. ToolCallRequest dari core.llm_client). Wajib ada agar
|
|
57
57
|
provider tidak 400 pada turn berikutnya (tool_call_id harus
|
|
58
58
|
merujuk ke assistant message sebelumnya).
|
|
59
|
+
|
|
60
|
+
Belajar dari opencode (AI SDK): tool adalah content part —
|
|
61
|
+
part kosong tidak pernah dikirim. Jadi kalau `calls` kosong,
|
|
62
|
+
key `tool_calls` di-OMIT total (bukan `[]`): provider strict
|
|
63
|
+
(400 `tool_calls must be non-empty if provided`) nolak array
|
|
64
|
+
kosong. Lihat issue #25.
|
|
59
65
|
"""
|
|
60
66
|
import json as _json
|
|
61
67
|
|
|
68
|
+
if not calls:
|
|
69
|
+
self._messages.append({"role": "assistant", "content": text or ""})
|
|
70
|
+
return
|
|
62
71
|
self._messages.append({
|
|
63
72
|
"role": "assistant",
|
|
64
73
|
"content": text or None,
|
|
@@ -78,7 +78,7 @@ termux_pip_guide() {
|
|
|
78
78
|
echo " (ringan: tanpa kompilasi Rust — dependensi wheel murni)"
|
|
79
79
|
echo ""
|
|
80
80
|
echo " 2. Install multacd:"
|
|
81
|
-
echo " pip install \"multacd==2.0.
|
|
81
|
+
echo " pip install \"multacd==2.0.0b5\""
|
|
82
82
|
echo ""
|
|
83
83
|
echo " 3. Jalankan: multacd"
|
|
84
84
|
echo ""
|
|
@@ -34,6 +34,10 @@ def test_plain_text_no_tool(sample_config, fake_llm_factory):
|
|
|
34
34
|
events = _run(_drain(run_agent("hi", ctx, sample_config, llm_client=fake)))
|
|
35
35
|
assert isinstance(events[-1], AgentDone)
|
|
36
36
|
assert "halo" in events[-1].text
|
|
37
|
+
# #25: assistant tanpa tool TIDAK boleh bawa key tool_calls
|
|
38
|
+
# (provider strict 400 kalau array kosong ikut terkirim).
|
|
39
|
+
assts = [m for m in ctx.get_messages() if m.get("role") == "assistant"]
|
|
40
|
+
assert assts and all("tool_calls" not in m for m in assts), assts
|
|
37
41
|
|
|
38
42
|
|
|
39
43
|
def test_single_auto_tool_runs(sample_config, fake_llm_factory, tmp_path):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|