toolang 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.
- {toolang-0.2.1 → toolang-0.2.2}/PKG-INFO +1 -1
- {toolang-0.2.1 → toolang-0.2.2}/pyproject.toml +1 -1
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/cli/progress.py +7 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/cli/toolang/app.py +43 -18
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/cli/utils.py +26 -17
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/config/log.py +21 -4
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/up.py +11 -1
- {toolang-0.2.1 → toolang-0.2.2}/tests/test_cli.py +103 -7
- {toolang-0.2.1 → toolang-0.2.2}/tests/test_log_config.py +36 -0
- {toolang-0.2.1 → toolang-0.2.2}/tests/test_run_queue_runner.py +46 -0
- {toolang-0.2.1 → toolang-0.2.2}/uv.lock +1 -1
- {toolang-0.2.1 → toolang-0.2.2}/.github/workflows/publish-pypi.yml +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/.gitignore +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/AGENTS.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/LICENSE +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/README.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/docs/api.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/docs/caps.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/docs/chat.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/docs/concepts.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/docs/execution.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/docs/ids.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/docs/index.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/docs/layout.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/docs/models.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/docs/plugins.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/docs/prepared-lock.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/docs/program.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/docs/selectors.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/docs/tasks.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/docs/tools.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/docs/webui-jobs.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/examples/alice.too +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/examples/invoke-playground.too +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/examples/invoke.fixed-model.too +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/examples/invoke.openrouter-smoke.too +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/examples/invoke.priority.too +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/examples/invoke.simulated-history.memory.too +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/examples/invoke.simulated-history.none.too +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/reference/README.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/reference/build.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/reference/config.toml +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/reference/templates/index.html.jinja2 +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/reference/templates/module.html.jinja2 +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/__init__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/agents.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/base/__init__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/base/error.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/base/examples/__init__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/base/examples/tools.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/base/protocols/__init__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/base/protocols/channel.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/base/protocols/loop.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/base/protocols/model.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/base/protocols/sandbox.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/base/protocols/tool.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/base/types/__init__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/base/types/channel.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/base/types/message.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/base/types/model.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/base/types/run.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/base/types/sandbox.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/base/types/tool.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/base/utils/__init__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/base/utils/channels.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/base/utils/function_tools.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/base/utils/tools.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/base/utils/typer_tools.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/caps.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/channels/__init__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/channels/telegram.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/cli/__init__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/cli/__main__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/cli/caps/__init__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/cli/caps/__main__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/cli/caps/app.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/cli/caps/commands.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/cli/invoke.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/cli/logo.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/cli/toolang/__init__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/cli/toolang/__main__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/cli/toolang/caps.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/cli/toolang/fmt.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/common/__init__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/common/ids.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/common/progress.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/common/template.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/components/__init__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/components/registry.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/components/router/__init__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/components/router/_streaming.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/components/router/chat.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/components/router/inspect/__init__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/components/router/inspect/_shared.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/components/router/inspect/agent.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/components/router/inspect/caps.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/components/router/inspect/execution.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/components/router/inspect/jobs.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/components/router/inspect/router.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/components/router/manage/__init__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/components/router/manage/caps.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/components/router/manage/jobs.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/components/router/manage/router.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/components/runner/__init__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/components/trigger/__init__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/components/trigger/poll.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/components/trigger/pulse.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/components/trigger/watch.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/config/__init__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/config/env.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/config/log_spec.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/config/plugins.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/config/web.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/execution/__init__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/execution/assembly.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/execution/binding.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/execution/context.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/execution/db.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/execution/detail.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/execution/effective.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/execution/events.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/execution/execute.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/execution/input.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/execution/model.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/execution/model_call.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/execution/prompts/__init__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/execution/prompts/context.default.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/execution/prompts/instruct.default.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/execution/records.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/execution/response.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/execution/runner.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/execution/stream.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/loops/__init__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/loops/basic.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/models/__init__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/models/adapters/__init__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/models/adapters/chat_completions.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/models/adapters/responses.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/models/config.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/models/discovery.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/models/errors.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/models/providers/__init__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/models/providers/custom.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/models/providers/deepseek.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/models/providers/google.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/models/providers/ollama.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/models/providers/openai.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/models/providers/openrouter.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/models/resolution.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/models/views.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/plugin.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/program.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/program_format.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/sandboxes/__init__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/sandboxes/docker.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/sandboxes/none.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/selectors.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/state/__init__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/state/durable.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/state/live.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/state/prepared.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/state/program.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/state/pulse.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/templates/__init__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/templates/agent.default.too +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/templates/chore.default.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/templates/prompt.default.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/templates/psyche.default.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/templates/service.default.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/templates/service.stdio.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/templates/skill.default.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/templates/task.default.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/tools/__init__.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/tools/agent_chat.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/tools/agent_state.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/tools/filesystem.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/tools/registry.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/tools/service_use.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/tools/shell.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/tools/views.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/tools/web_search.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/src/toolang/work.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/syntax.too +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/tests/fixtures/remote-prompt/rewrite.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/tests/fixtures/remote-psyche/reviewer.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/tests/fixtures/remote-service/github.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/tests/fixtures/remote-skill/pdf-processing/SKILL.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/tests/fixtures/remote-skill/pdf-processing/assets/template.md +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/tests/fixtures/sample.too +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/tests/fixtures/source_only.too +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/tests/test_agent_state_tool.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/tests/test_channels.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/tests/test_execution_template.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/tests/test_ids.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/tests/test_models.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/tests/test_plugin_tools.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/tests/test_prepared.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/tests/test_program.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/tests/test_program_format.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/tests/test_sandboxes.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/tests/test_selectors.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/tests/test_tools.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/tests/test_typer_tools.py +0 -0
- {toolang-0.2.1 → toolang-0.2.2}/tests/test_work.py +0 -0
|
@@ -83,6 +83,7 @@ class CliProgress:
|
|
|
83
83
|
for line in self._lines():
|
|
84
84
|
print(line, file=self._stream)
|
|
85
85
|
self._print_summary()
|
|
86
|
+
self._reset_terminal_style()
|
|
86
87
|
|
|
87
88
|
def interrupt(self) -> None:
|
|
88
89
|
with self._lock:
|
|
@@ -290,6 +291,12 @@ class CliProgress:
|
|
|
290
291
|
for summary in summaries:
|
|
291
292
|
self._console.print(Text(summary, style="dim"))
|
|
292
293
|
|
|
294
|
+
def _reset_terminal_style(self) -> None:
|
|
295
|
+
if not self._console.is_terminal:
|
|
296
|
+
return
|
|
297
|
+
self._stream.write("\x1b[0m")
|
|
298
|
+
self._stream.flush()
|
|
299
|
+
|
|
293
300
|
def _live_text(self) -> Text:
|
|
294
301
|
text = Text()
|
|
295
302
|
agent_items = [item for item in self._items.values() if item.kind == "agent"]
|
|
@@ -38,7 +38,6 @@ from ..utils import (
|
|
|
38
38
|
_required_prefix_agent,
|
|
39
39
|
_required_runtime_agent,
|
|
40
40
|
_runtime_environ_for_agent,
|
|
41
|
-
_runtime_components,
|
|
42
41
|
_runtime_value,
|
|
43
42
|
_toolang_root,
|
|
44
43
|
_ui_base_url,
|
|
@@ -401,6 +400,7 @@ def info_agent(
|
|
|
401
400
|
("Home", str(agents.agent_home(root, agent_name))),
|
|
402
401
|
("Caps", _info_caps_summary(root, agent_name)),
|
|
403
402
|
("Jobs", _info_jobs_summary(root, agent_name)),
|
|
403
|
+
("Tools", _info_tools_summary(root, agent_name)),
|
|
404
404
|
("Models", _info_models_summary(root, agent_name, runtime_state=runtime_state, running=status.status != "stopped")),
|
|
405
405
|
("Status", status_value),
|
|
406
406
|
]
|
|
@@ -410,16 +410,12 @@ def info_agent(
|
|
|
410
410
|
return
|
|
411
411
|
if status.sandbox:
|
|
412
412
|
rows.append(("Sandbox", status.sandbox))
|
|
413
|
-
if status.status == "running":
|
|
414
|
-
loops_text = _runtime_components(runtime_state)
|
|
415
|
-
if loops_text is not None:
|
|
416
|
-
rows.append(("Components", loops_text))
|
|
417
413
|
message = _runtime_value(runtime_state.get("message"))
|
|
418
414
|
pid_text = agents.runtime_pid_label(runtime_state)
|
|
419
415
|
if pid_text is not None and status.status != "stopped":
|
|
420
416
|
rows.append(("PID", pid_text))
|
|
421
|
-
if status.
|
|
422
|
-
rows.append(("API", status.
|
|
417
|
+
if status.endpoint:
|
|
418
|
+
rows.append(("API", status.endpoint))
|
|
423
419
|
if status.webui_url:
|
|
424
420
|
rows.append(("WebUI", status.webui_url))
|
|
425
421
|
if status.status == "running" and started_at != "-":
|
|
@@ -875,19 +871,46 @@ def _info_models_summary(
|
|
|
875
871
|
if running:
|
|
876
872
|
raw_models = runtime_state.get("models")
|
|
877
873
|
if isinstance(raw_models, list):
|
|
878
|
-
|
|
874
|
+
selectors = tuple(
|
|
879
875
|
str(item).strip()
|
|
880
876
|
for item in raw_models
|
|
881
877
|
if isinstance(item, str) and item.strip()
|
|
882
878
|
)
|
|
883
|
-
if
|
|
884
|
-
return
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
879
|
+
if selectors:
|
|
880
|
+
return _model_count_summary(
|
|
881
|
+
toolang_root,
|
|
882
|
+
agent_name,
|
|
883
|
+
environ=dict(os.environ),
|
|
884
|
+
selectors=selectors,
|
|
885
|
+
)
|
|
886
|
+
selectors = agent_up.load_default_models(toolang_root, agent_name)
|
|
887
|
+
return _model_count_summary(
|
|
888
|
+
toolang_root,
|
|
889
|
+
agent_name,
|
|
890
|
+
environ=dict(os.environ),
|
|
891
|
+
selectors=selectors,
|
|
892
|
+
)
|
|
893
|
+
|
|
889
894
|
|
|
890
|
-
|
|
895
|
+
def _info_tools_summary(toolang_root: Path, agent_name: str) -> str:
|
|
896
|
+
rows = _tool_rows(toolang_root, dict(os.environ), agent_name=agent_name)
|
|
897
|
+
set_count = len({namespace for namespace, _tool, _description in rows})
|
|
898
|
+
return f"{len(rows)} {'tool' if len(rows) == 1 else 'tools'}, {set_count} {'set' if set_count == 1 else 'sets'}"
|
|
899
|
+
|
|
900
|
+
|
|
901
|
+
def _model_count_summary(
|
|
902
|
+
toolang_root: Path,
|
|
903
|
+
agent_name: str,
|
|
904
|
+
*,
|
|
905
|
+
environ: dict[str, str],
|
|
906
|
+
selectors: Sequence[str] = (),
|
|
907
|
+
) -> str:
|
|
908
|
+
rows = _model_rows(toolang_root, environ, agent_name=agent_name, model_selectors=selectors)
|
|
909
|
+
provider_count = len({provider for _model, provider, _details in rows})
|
|
910
|
+
return (
|
|
911
|
+
f"{len(rows)} {'model' if len(rows) == 1 else 'models'}, "
|
|
912
|
+
f"{provider_count} {'provider' if provider_count == 1 else 'providers'}"
|
|
913
|
+
)
|
|
891
914
|
|
|
892
915
|
|
|
893
916
|
model_app = typer.Typer(
|
|
@@ -1032,13 +1055,14 @@ def _model_rows(
|
|
|
1032
1055
|
root: Path,
|
|
1033
1056
|
environ: dict[str, str],
|
|
1034
1057
|
*,
|
|
1058
|
+
agent_name: str = "",
|
|
1035
1059
|
model_selectors: Sequence[str] = (),
|
|
1036
1060
|
) -> list[tuple[str, str, str]]:
|
|
1037
1061
|
from ...models.config import load_model_aliases
|
|
1038
1062
|
from ...models.views import model_list_rows
|
|
1039
1063
|
|
|
1040
|
-
providers = agent_up.load_model_providers(root,
|
|
1041
|
-
aliases = load_model_aliases(root,
|
|
1064
|
+
providers = agent_up.load_model_providers(root, agent_name)
|
|
1065
|
+
aliases = load_model_aliases(root, agent_name)
|
|
1042
1066
|
return model_list_rows(
|
|
1043
1067
|
providers=providers,
|
|
1044
1068
|
aliases=aliases,
|
|
@@ -1066,12 +1090,13 @@ def _tool_rows(
|
|
|
1066
1090
|
root: Path,
|
|
1067
1091
|
environ: dict[str, str],
|
|
1068
1092
|
*,
|
|
1093
|
+
agent_name: str = "",
|
|
1069
1094
|
tool_selectors: Sequence[str] = (),
|
|
1070
1095
|
) -> list[tuple[str, str, str]]:
|
|
1071
1096
|
from ...config.plugins import load_tool_plugin_config
|
|
1072
1097
|
from ...tools.views import tool_list_rows
|
|
1073
1098
|
|
|
1074
|
-
config = load_tool_plugin_config(root,
|
|
1099
|
+
config = load_tool_plugin_config(root, agent_name, environ=environ)
|
|
1075
1100
|
tools = agent_up.load_tool_plugins(config=config)
|
|
1076
1101
|
sources = _plugin_source_by_name("toolang.tool")
|
|
1077
1102
|
return tool_list_rows(
|
|
@@ -30,6 +30,7 @@ if TYPE_CHECKING:
|
|
|
30
30
|
# weight so usage notes remain easy to read in terminal help output.
|
|
31
31
|
setattr(typer_rich_utils, "STYLE_HELPTEXT", "")
|
|
32
32
|
_TABLE_CONSOLE = Console(highlight=False, width=4096)
|
|
33
|
+
_INFO_CONSOLE = Console(highlight=False)
|
|
33
34
|
TableJustify = Literal["default", "left", "center", "right", "full"]
|
|
34
35
|
_AGENT_AVATAR_CACHE: str | None = None
|
|
35
36
|
_PALETTE_STYLES_CACHE: tuple[tuple[str, ...], tuple[str, ...]] | None = None
|
|
@@ -412,31 +413,39 @@ def _echo_pairs_table(
|
|
|
412
413
|
collapse_padding=True,
|
|
413
414
|
)
|
|
414
415
|
table.add_column("FIELD", no_wrap=True, style="bold bright_cyan")
|
|
415
|
-
table.add_column("VALUE", no_wrap=False, style="white")
|
|
416
|
+
table.add_column("VALUE", no_wrap=False, style="white", overflow="fold")
|
|
416
417
|
for key, value in rows:
|
|
417
418
|
table.add_row(Text(key), _styled_info_value(key, value))
|
|
418
419
|
typer.echo()
|
|
419
420
|
if avatar is None:
|
|
420
421
|
if title is None:
|
|
421
|
-
|
|
422
|
+
_INFO_CONSOLE.print(table)
|
|
422
423
|
else:
|
|
423
|
-
|
|
424
|
-
|
|
424
|
+
_INFO_CONSOLE.print(_info_title_block(title))
|
|
425
|
+
_INFO_CONSOLE.print(table)
|
|
425
426
|
else:
|
|
426
|
-
layout = Table.grid(padding=(0, 4))
|
|
427
|
-
layout.add_column(no_wrap=True, ratio=0)
|
|
428
|
-
layout.add_column(no_wrap=False, ratio=1)
|
|
429
|
-
right = Table.grid(padding=(0, 0))
|
|
430
|
-
right.add_column(no_wrap=False)
|
|
431
427
|
avatar_text = _rainbow_avatar_text(avatar)
|
|
432
|
-
if
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
428
|
+
if _INFO_CONSOLE.width < 100:
|
|
429
|
+
_INFO_CONSOLE.print(avatar_text)
|
|
430
|
+
if title is not None:
|
|
431
|
+
_INFO_CONSOLE.print(_info_title_block(title))
|
|
432
|
+
_INFO_CONSOLE.print(table)
|
|
433
|
+
_INFO_CONSOLE.print(Text(""))
|
|
434
|
+
_INFO_CONSOLE.print(_palette_block())
|
|
435
|
+
else:
|
|
436
|
+
layout = Table.grid(padding=(0, 4))
|
|
437
|
+
layout.add_column(no_wrap=True, ratio=0)
|
|
438
|
+
layout.add_column(no_wrap=False, ratio=1)
|
|
439
|
+
right = Table.grid(padding=(0, 0))
|
|
440
|
+
right.add_column(no_wrap=False)
|
|
441
|
+
if title is not None:
|
|
442
|
+
right.add_row(_info_title_block(title))
|
|
443
|
+
avatar_text = _rainbow_avatar_text("\n" + avatar)
|
|
444
|
+
right.add_row(table)
|
|
445
|
+
right.add_row(Text(""))
|
|
446
|
+
right.add_row(_palette_block())
|
|
447
|
+
layout.add_row(avatar_text, right)
|
|
448
|
+
_INFO_CONSOLE.print(layout)
|
|
440
449
|
typer.echo()
|
|
441
450
|
|
|
442
451
|
|
|
@@ -8,6 +8,7 @@ import logging
|
|
|
8
8
|
import logging.config
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
import re
|
|
11
|
+
import sys
|
|
11
12
|
from typing import Any, Literal, cast
|
|
12
13
|
|
|
13
14
|
from .log_spec import (
|
|
@@ -72,13 +73,21 @@ class MessageRegexFilter(logging.Filter):
|
|
|
72
73
|
return bool(self._pattern.search(record.getMessage()))
|
|
73
74
|
|
|
74
75
|
|
|
75
|
-
def build_uvicorn_log_config(
|
|
76
|
+
def build_uvicorn_log_config(
|
|
77
|
+
*,
|
|
78
|
+
spec: LogSpec | None = None,
|
|
79
|
+
level: str = DEFAULT_LOG_LEVEL,
|
|
80
|
+
default_use_colors: bool | None = None,
|
|
81
|
+
access_use_colors: bool | None = None,
|
|
82
|
+
) -> dict[str, object]:
|
|
76
83
|
"""Build one logging config shared by Uvicorn and runtime loggers."""
|
|
77
84
|
|
|
78
85
|
ensure_custom_levels()
|
|
79
86
|
log_spec = spec or resolve_log_spec(cli_value=level, environ={}, default=DEFAULT_LOG_LEVEL)
|
|
80
87
|
root_level = level_name(log_spec.root_level)
|
|
81
88
|
handler_level = level_name(log_spec.handler_level)
|
|
89
|
+
default_colors = _stream_uses_colors(sys.stderr) if default_use_colors is None else default_use_colors
|
|
90
|
+
access_colors = _stream_uses_colors(sys.stdout) if access_use_colors is None else access_use_colors
|
|
82
91
|
return {
|
|
83
92
|
"version": 1,
|
|
84
93
|
"disable_existing_loggers": False,
|
|
@@ -87,13 +96,13 @@ def build_uvicorn_log_config(*, spec: LogSpec | None = None, level: str = DEFAUL
|
|
|
87
96
|
"()": "uvicorn.logging.DefaultFormatter",
|
|
88
97
|
"fmt": DEFAULT_LOG_FORMAT,
|
|
89
98
|
"datefmt": DEFAULT_LOG_DATE_FORMAT,
|
|
90
|
-
"use_colors":
|
|
99
|
+
"use_colors": default_colors,
|
|
91
100
|
},
|
|
92
101
|
"access": {
|
|
93
102
|
"()": "uvicorn.logging.AccessFormatter",
|
|
94
103
|
"fmt": DEFAULT_ACCESS_LOG_FORMAT,
|
|
95
104
|
"datefmt": DEFAULT_LOG_DATE_FORMAT,
|
|
96
|
-
"use_colors":
|
|
105
|
+
"use_colors": access_colors,
|
|
97
106
|
},
|
|
98
107
|
},
|
|
99
108
|
"filters": {
|
|
@@ -169,7 +178,11 @@ def configure_logging(
|
|
|
169
178
|
environ={} if environ is None else environ,
|
|
170
179
|
default=DEFAULT_LOG_LEVEL,
|
|
171
180
|
)
|
|
172
|
-
config = build_uvicorn_log_config(
|
|
181
|
+
config = build_uvicorn_log_config(
|
|
182
|
+
spec=log_spec,
|
|
183
|
+
default_use_colors=False if log_path is not None else None,
|
|
184
|
+
access_use_colors=False if log_path is not None else None,
|
|
185
|
+
)
|
|
173
186
|
if log_path is not None:
|
|
174
187
|
log_path.parent.mkdir(parents=True, exist_ok=True)
|
|
175
188
|
handlers = cast(dict[str, object], config.get("handlers", {}))
|
|
@@ -238,6 +251,10 @@ def _logger_level_name(target: str, spec: LogSpec, *, default: int) -> str:
|
|
|
238
251
|
return level_name(default if level is None else level)
|
|
239
252
|
|
|
240
253
|
|
|
254
|
+
def _stream_uses_colors(stream: object) -> bool:
|
|
255
|
+
return bool(getattr(stream, "isatty", lambda: False)())
|
|
256
|
+
|
|
257
|
+
|
|
241
258
|
def _install_message_filter(filter_obj: MessageRegexFilter) -> None:
|
|
242
259
|
seen: set[int] = set()
|
|
243
260
|
root = logging.getLogger()
|
|
@@ -46,6 +46,7 @@ from toolang.tools.registry import (
|
|
|
46
46
|
from .config.log import (
|
|
47
47
|
DEFAULT_LOG_LEVEL,
|
|
48
48
|
build_uvicorn_log_config,
|
|
49
|
+
configure_logging,
|
|
49
50
|
configure_logging_plan,
|
|
50
51
|
resolve_agent_logging,
|
|
51
52
|
)
|
|
@@ -777,6 +778,8 @@ def _up_local(
|
|
|
777
778
|
progress: ProgressSink | None = None,
|
|
778
779
|
prepared_state: PreparedState | None = None,
|
|
779
780
|
) -> int:
|
|
781
|
+
runtime_log_spec = _runtime_log_spec_value(log_spec, environ)
|
|
782
|
+
configure_logging(spec=runtime_log_spec, environ=environ)
|
|
780
783
|
loop_intervals_ms = dict(DEFAULT_TRIGGER_INTERVAL_MS)
|
|
781
784
|
for component_name in component_group(TRIGGER_COMPONENTS, "trigger"):
|
|
782
785
|
if component_name in loop_intervals_ms and loop_intervals_ms[component_name] <= 0:
|
|
@@ -884,7 +887,7 @@ def _up_local(
|
|
|
884
887
|
app,
|
|
885
888
|
host=host,
|
|
886
889
|
port=port,
|
|
887
|
-
log_config=build_uvicorn_log_config(level=
|
|
890
|
+
log_config=build_uvicorn_log_config(level=runtime_log_spec or DEFAULT_LOG_LEVEL),
|
|
888
891
|
shutdown_signal=shutdown_signal,
|
|
889
892
|
on_starting=lambda: logger.info(
|
|
890
893
|
"Agent starting root=%s trigger=%s runner=%s router=%s",
|
|
@@ -916,6 +919,13 @@ def _up_local(
|
|
|
916
919
|
return 0
|
|
917
920
|
|
|
918
921
|
|
|
922
|
+
def _runtime_log_spec_value(log_spec: str | None, environ: Mapping[str, str]) -> str | None:
|
|
923
|
+
if isinstance(log_spec, str) and log_spec.strip():
|
|
924
|
+
return log_spec.strip()
|
|
925
|
+
env_spec = environ.get(PY_LOG_ENV_VAR, "").strip()
|
|
926
|
+
return env_spec or None
|
|
927
|
+
|
|
928
|
+
|
|
919
929
|
def _log_state_loaded(context: UptimeContext) -> None:
|
|
920
930
|
state_logger.info(
|
|
921
931
|
"Agent loaded state=%s models=%s tools=%s psyches=%s skills=%s services=%s",
|
|
@@ -20,6 +20,7 @@ from toolang.base.types.tool import ToolContext, ToolDefinition
|
|
|
20
20
|
import toolang.cli.toolang.app as cli
|
|
21
21
|
import toolang.cli.caps.app as caps_cli
|
|
22
22
|
import toolang.cli.caps.commands as caps_commands
|
|
23
|
+
import toolang.cli.utils as cli_utils
|
|
23
24
|
from toolang.cli.progress import CliProgress
|
|
24
25
|
from toolang.components.trigger import watch
|
|
25
26
|
from toolang.config.log import DEFAULT_AGENT_LOG_SPEC
|
|
@@ -2623,6 +2624,57 @@ def test_cli_info_shows_agent_details(tmp_path: Path, monkeypatch) -> None:
|
|
|
2623
2624
|
"_utc_now",
|
|
2624
2625
|
lambda: datetime(2026, 4, 8, 12, 0, 0, tzinfo=timezone.utc),
|
|
2625
2626
|
)
|
|
2627
|
+
monkeypatch.setattr(
|
|
2628
|
+
cli.agent_up,
|
|
2629
|
+
"load_model_providers",
|
|
2630
|
+
lambda *_args: {
|
|
2631
|
+
"openai": _FakeModelProvider(
|
|
2632
|
+
name="openai",
|
|
2633
|
+
models=(
|
|
2634
|
+
ModelInfo(
|
|
2635
|
+
ref="openai/o3",
|
|
2636
|
+
provider="openai",
|
|
2637
|
+
name="o3",
|
|
2638
|
+
model="o3",
|
|
2639
|
+
selectors=("o3", "openai/o3"),
|
|
2640
|
+
adapter="responses",
|
|
2641
|
+
),
|
|
2642
|
+
ModelInfo(
|
|
2643
|
+
ref="openai/gpt-5",
|
|
2644
|
+
provider="openai",
|
|
2645
|
+
name="gpt-5",
|
|
2646
|
+
model="gpt-5",
|
|
2647
|
+
selectors=("gpt-5", "openai/gpt-5"),
|
|
2648
|
+
adapter="responses",
|
|
2649
|
+
),
|
|
2650
|
+
),
|
|
2651
|
+
),
|
|
2652
|
+
},
|
|
2653
|
+
)
|
|
2654
|
+
monkeypatch.setattr(
|
|
2655
|
+
cli.agent_up,
|
|
2656
|
+
"load_tool_plugins",
|
|
2657
|
+
lambda *, config=None: {
|
|
2658
|
+
"filesystem__read_text": _FakeLoadedTool(
|
|
2659
|
+
plugin_name="filesystem",
|
|
2660
|
+
leaf_name="read_text",
|
|
2661
|
+
description="Read one text file.",
|
|
2662
|
+
),
|
|
2663
|
+
"shell__execute": _FakeLoadedTool(
|
|
2664
|
+
plugin_name="shell",
|
|
2665
|
+
leaf_name="execute",
|
|
2666
|
+
description="Run one shell command.",
|
|
2667
|
+
),
|
|
2668
|
+
},
|
|
2669
|
+
)
|
|
2670
|
+
monkeypatch.setattr(
|
|
2671
|
+
cli.agent_up,
|
|
2672
|
+
"list_plugin_infos",
|
|
2673
|
+
lambda *, group: [
|
|
2674
|
+
cli.agent_up.PluginInfo(name="filesystem", source="built-in"),
|
|
2675
|
+
cli.agent_up.PluginInfo(name="shell", source="external"),
|
|
2676
|
+
],
|
|
2677
|
+
)
|
|
2626
2678
|
caps.put_local_entry_text(
|
|
2627
2679
|
toolang_root,
|
|
2628
2680
|
"alice",
|
|
@@ -2677,7 +2729,8 @@ def test_cli_info_shows_agent_details(tmp_path: Path, monkeypatch) -> None:
|
|
|
2677
2729
|
assert "alice" in result.stdout
|
|
2678
2730
|
assert "-----" in result.stdout
|
|
2679
2731
|
assert "Home" in result.stdout
|
|
2680
|
-
assert
|
|
2732
|
+
assert result.stdout.index("▄▄▄▄▄▄▄▄▄") < result.stdout.index("ALICE") < result.stdout.index("Home")
|
|
2733
|
+
assert str(toolang_root / "agents" / "alice") in "".join(result.stdout.split())
|
|
2681
2734
|
assert "ROOM" not in result.stdout
|
|
2682
2735
|
assert "PROGRAM" not in result.stdout
|
|
2683
2736
|
assert "RUNTIME" not in result.stdout
|
|
@@ -2692,13 +2745,16 @@ def test_cli_info_shows_agent_details(tmp_path: Path, monkeypatch) -> None:
|
|
|
2692
2745
|
assert "1 chore" in result.stdout
|
|
2693
2746
|
assert "1 task" in result.stdout
|
|
2694
2747
|
assert "Models" in result.stdout
|
|
2695
|
-
assert "
|
|
2748
|
+
assert "2 models, 1 provider" in result.stdout
|
|
2749
|
+
assert "Tools" in result.stdout
|
|
2750
|
+
assert "2 tools, 2 sets" in result.stdout
|
|
2751
|
+
assert result.stdout.index("Tools") < result.stdout.index("Models")
|
|
2696
2752
|
assert "Status" in result.stdout
|
|
2697
2753
|
assert "running (up a day)" in result.stdout
|
|
2698
2754
|
assert "Sandbox" in result.stdout
|
|
2699
2755
|
assert "none" in result.stdout
|
|
2700
|
-
assert "Components" in result.stdout
|
|
2701
|
-
assert "router.chat, runner.chat, trigger.pulse" in result.stdout
|
|
2756
|
+
assert "Components" not in result.stdout
|
|
2757
|
+
assert "router.chat, runner.chat, trigger.pulse" not in result.stdout
|
|
2702
2758
|
assert "PID" in result.stdout
|
|
2703
2759
|
assert str(os.getpid()) in result.stdout
|
|
2704
2760
|
assert "Started" in result.stdout
|
|
@@ -2707,7 +2763,8 @@ def test_cli_info_shows_agent_details(tmp_path: Path, monkeypatch) -> None:
|
|
|
2707
2763
|
assert "ONLINE" not in result.stdout
|
|
2708
2764
|
assert "ENDPOINT" not in result.stdout
|
|
2709
2765
|
assert "API" in result.stdout
|
|
2710
|
-
assert "http://127.0.0.1:8765
|
|
2766
|
+
assert "http://127.0.0.1:8765" in result.stdout
|
|
2767
|
+
assert "http://127.0.0.1:8765/docs" not in result.stdout
|
|
2711
2768
|
assert "WebUI" in result.stdout
|
|
2712
2769
|
assert "https://too.run/8765" in result.stdout
|
|
2713
2770
|
assert "Updated" not in result.stdout
|
|
@@ -2716,6 +2773,12 @@ def test_cli_info_shows_agent_details(tmp_path: Path, monkeypatch) -> None:
|
|
|
2716
2773
|
assert result.stdout.index("Started") < result.stdout.index("Created")
|
|
2717
2774
|
|
|
2718
2775
|
|
|
2776
|
+
def test_cli_info_console_uses_terminal_width(monkeypatch) -> None:
|
|
2777
|
+
monkeypatch.setenv("COLUMNS", "72")
|
|
2778
|
+
|
|
2779
|
+
assert cli_utils._INFO_CONSOLE.width == 72
|
|
2780
|
+
|
|
2781
|
+
|
|
2719
2782
|
def test_cli_info_reads_cap_counts_from_prepared_locks(tmp_path: Path, monkeypatch) -> None:
|
|
2720
2783
|
from toolang.state.prepared import write_prepared_lock
|
|
2721
2784
|
|
|
@@ -2878,7 +2941,7 @@ def test_cli_info_for_running_docker_sandbox_shows_container_pid(tmp_path: Path,
|
|
|
2878
2941
|
assert result.stdout.index("PID") < result.stdout.index("API")
|
|
2879
2942
|
|
|
2880
2943
|
|
|
2881
|
-
def test_cli_info_prefers_runtime_models_for_active_agent(tmp_path: Path) -> None:
|
|
2944
|
+
def test_cli_info_prefers_runtime_models_for_active_agent(tmp_path: Path, monkeypatch) -> None:
|
|
2882
2945
|
toolang_root = tmp_path / "toolang"
|
|
2883
2946
|
agents.create_agent(toolang_root, "alice")
|
|
2884
2947
|
(toolang_root / "agents" / "alice" / "config.toml").write_text(
|
|
@@ -2886,6 +2949,39 @@ def test_cli_info_prefers_runtime_models_for_active_agent(tmp_path: Path) -> Non
|
|
|
2886
2949
|
'default = ["o3", "gpt-5"]\n',
|
|
2887
2950
|
encoding="utf-8",
|
|
2888
2951
|
)
|
|
2952
|
+
monkeypatch.setattr(
|
|
2953
|
+
cli.agent_up,
|
|
2954
|
+
"load_model_providers",
|
|
2955
|
+
lambda *_args: {
|
|
2956
|
+
"anthropic": _FakeModelProvider(
|
|
2957
|
+
name="anthropic",
|
|
2958
|
+
models=(
|
|
2959
|
+
ModelInfo(
|
|
2960
|
+
ref="anthropic/claude",
|
|
2961
|
+
provider="anthropic",
|
|
2962
|
+
name="claude",
|
|
2963
|
+
model="claude",
|
|
2964
|
+
selectors=("claude", "anthropic/claude"),
|
|
2965
|
+
adapter="responses",
|
|
2966
|
+
),
|
|
2967
|
+
),
|
|
2968
|
+
),
|
|
2969
|
+
"openai": _FakeModelProvider(
|
|
2970
|
+
name="openai",
|
|
2971
|
+
models=(
|
|
2972
|
+
ModelInfo(
|
|
2973
|
+
ref="openai/gpt-5",
|
|
2974
|
+
provider="openai",
|
|
2975
|
+
name="gpt-5",
|
|
2976
|
+
model="gpt-5",
|
|
2977
|
+
selectors=("gpt-5", "openai/gpt-5"),
|
|
2978
|
+
adapter="responses",
|
|
2979
|
+
),
|
|
2980
|
+
),
|
|
2981
|
+
),
|
|
2982
|
+
},
|
|
2983
|
+
)
|
|
2984
|
+
monkeypatch.setattr(cli.agent_up, "load_tool_plugins", lambda *, config=None: {})
|
|
2889
2985
|
agents.write_runtime_state(
|
|
2890
2986
|
toolang_root,
|
|
2891
2987
|
"alice",
|
|
@@ -2905,7 +3001,7 @@ def test_cli_info_prefers_runtime_models_for_active_agent(tmp_path: Path) -> Non
|
|
|
2905
3001
|
|
|
2906
3002
|
assert result.exit_code == 0
|
|
2907
3003
|
assert "Models" in result.stdout
|
|
2908
|
-
assert "
|
|
3004
|
+
assert "2 models, 2 providers" in result.stdout
|
|
2909
3005
|
|
|
2910
3006
|
|
|
2911
3007
|
def test_cli_channel_list_shows_installed_channels(monkeypatch) -> None:
|
|
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import logging
|
|
4
4
|
import logging.config
|
|
5
5
|
import re
|
|
6
|
+
import sys
|
|
6
7
|
from typing import cast
|
|
7
8
|
|
|
8
9
|
from toolang.config.log import (
|
|
@@ -70,6 +71,25 @@ def test_build_uvicorn_log_config_keeps_http_debug_off_when_toolang_debug_is_ena
|
|
|
70
71
|
assert loggers["httpcore"]["level"] == "OFF"
|
|
71
72
|
|
|
72
73
|
|
|
74
|
+
def test_build_uvicorn_log_config_uses_formatter_stream_colors(monkeypatch) -> None:
|
|
75
|
+
class Tty:
|
|
76
|
+
def isatty(self) -> bool:
|
|
77
|
+
return True
|
|
78
|
+
|
|
79
|
+
class Plain:
|
|
80
|
+
def isatty(self) -> bool:
|
|
81
|
+
return False
|
|
82
|
+
|
|
83
|
+
monkeypatch.setattr(sys, "stderr", Tty())
|
|
84
|
+
monkeypatch.setattr(sys, "stdout", Plain())
|
|
85
|
+
|
|
86
|
+
config = build_uvicorn_log_config()
|
|
87
|
+
|
|
88
|
+
formatters = cast(dict[str, dict[str, object]], config["formatters"])
|
|
89
|
+
assert formatters["default"]["use_colors"] is True
|
|
90
|
+
assert formatters["access"]["use_colors"] is False
|
|
91
|
+
|
|
92
|
+
|
|
73
93
|
def test_parse_log_level_accepts_warn_alias() -> None:
|
|
74
94
|
assert parse_log_level("warn") == logging.WARNING
|
|
75
95
|
|
|
@@ -148,6 +168,22 @@ def test_configure_logging_supports_off_level(monkeypatch) -> None:
|
|
|
148
168
|
assert loggers["httpx"]["level"] == "OFF"
|
|
149
169
|
|
|
150
170
|
|
|
171
|
+
def test_configure_logging_disables_colors_for_file_logs(monkeypatch, tmp_path) -> None:
|
|
172
|
+
captured: dict[str, object] = {}
|
|
173
|
+
|
|
174
|
+
def fake_dict_config(config: dict[str, object]) -> None:
|
|
175
|
+
captured["config"] = config
|
|
176
|
+
|
|
177
|
+
monkeypatch.setattr(logging.config, "dictConfig", fake_dict_config)
|
|
178
|
+
|
|
179
|
+
configure_logging(spec="toolang.run=debug", environ={}, log_path=tmp_path / "agent.log")
|
|
180
|
+
|
|
181
|
+
config = cast(dict[str, object], captured["config"])
|
|
182
|
+
formatters = cast(dict[str, dict[str, object]], config["formatters"])
|
|
183
|
+
assert formatters["default"]["use_colors"] is False
|
|
184
|
+
assert formatters["access"]["use_colors"] is False
|
|
185
|
+
|
|
186
|
+
|
|
151
187
|
def test_resolve_agent_logging_defaults_run_and_start_to_agent_spec(tmp_path) -> None:
|
|
152
188
|
agent_log_path = tmp_path / "agent.log"
|
|
153
189
|
|
|
@@ -84,6 +84,7 @@ from toolang.caps import (
|
|
|
84
84
|
remove_remote_entry,
|
|
85
85
|
)
|
|
86
86
|
from toolang.config.plugins import ChannelBinding
|
|
87
|
+
from toolang.config.log_spec import PY_LOG_ENV_VAR
|
|
87
88
|
from toolang.execution import execute as run_execute_module
|
|
88
89
|
from toolang.execution.input import RunInput, bind_run_request
|
|
89
90
|
from toolang.execution.runner import QueueRunner, RunOutcome, RunRequest, RunSubmission
|
|
@@ -2301,6 +2302,7 @@ def test_up_logs_runtime_urls_after_start_and_stop(tmp_path: Path, monkeypatch,
|
|
|
2301
2302
|
on_stopped()
|
|
2302
2303
|
|
|
2303
2304
|
monkeypatch.setattr("toolang.up._run_uvicorn_app", fake_run_uvicorn_app)
|
|
2305
|
+
monkeypatch.setattr("toolang.up.configure_logging", lambda **_kwargs: None)
|
|
2304
2306
|
caplog.set_level(logging.INFO, logger="toolang.runtime")
|
|
2305
2307
|
|
|
2306
2308
|
result = run_experiments_up(
|
|
@@ -2330,6 +2332,50 @@ def test_up_logs_runtime_urls_after_start_and_stop(tmp_path: Path, monkeypatch,
|
|
|
2330
2332
|
assert color_messages[1] == "Agent started webui=\x1b[1m%s\x1b[0m"
|
|
2331
2333
|
|
|
2332
2334
|
|
|
2335
|
+
def test_local_runtime_configures_logging_before_state_loaded(tmp_path: Path, monkeypatch) -> None:
|
|
2336
|
+
toolang_root = tmp_path / "toolang"
|
|
2337
|
+
agents.create_agent(toolang_root, "alice")
|
|
2338
|
+
order: list[str] = []
|
|
2339
|
+
captured: dict[str, object] = {}
|
|
2340
|
+
|
|
2341
|
+
def fake_configure_logging(*, spec: str | None, environ: dict[str, str], log_path=None) -> None:
|
|
2342
|
+
del log_path
|
|
2343
|
+
order.append("configure")
|
|
2344
|
+
captured["spec"] = spec
|
|
2345
|
+
captured["environ"] = environ
|
|
2346
|
+
|
|
2347
|
+
def fake_log_state_loaded(context: UptimeContext) -> None:
|
|
2348
|
+
del context
|
|
2349
|
+
order.append("state")
|
|
2350
|
+
|
|
2351
|
+
def fake_run_uvicorn_app(*args, **kwargs) -> None:
|
|
2352
|
+
del args, kwargs
|
|
2353
|
+
order.append("run")
|
|
2354
|
+
|
|
2355
|
+
monkeypatch.setattr(up_module, "configure_logging", fake_configure_logging)
|
|
2356
|
+
monkeypatch.setattr(up_module, "_log_state_loaded", fake_log_state_loaded)
|
|
2357
|
+
monkeypatch.setattr(up_module, "_run_uvicorn_app", fake_run_uvicorn_app)
|
|
2358
|
+
|
|
2359
|
+
result = up_module._up_local(
|
|
2360
|
+
toolang_root=toolang_root,
|
|
2361
|
+
agent_name="alice",
|
|
2362
|
+
host="127.0.0.1",
|
|
2363
|
+
endpoint_host="localhost",
|
|
2364
|
+
port=8765,
|
|
2365
|
+
enabled_components=("router.inspect",),
|
|
2366
|
+
environ={PY_LOG_ENV_VAR: "toolang.state=info"},
|
|
2367
|
+
sandbox_child=False,
|
|
2368
|
+
model_selectors=(),
|
|
2369
|
+
tool_selectors=None,
|
|
2370
|
+
cap_selectors=(),
|
|
2371
|
+
log_spec=None,
|
|
2372
|
+
)
|
|
2373
|
+
|
|
2374
|
+
assert result == 0
|
|
2375
|
+
assert order[:2] == ["configure", "state"]
|
|
2376
|
+
assert captured["spec"] == "toolang.state=info"
|
|
2377
|
+
|
|
2378
|
+
|
|
2333
2379
|
def test_state_loaded_log_counts_selectable_models(tmp_path: Path, caplog) -> None:
|
|
2334
2380
|
toolang_root = tmp_path / "toolang"
|
|
2335
2381
|
agents.create_agent(toolang_root, "alice")
|
|
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
|