axio-tui 0.2.2__tar.gz → 0.3.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.
Files changed (28) hide show
  1. axio_tui-0.3.2/.gitignore +12 -0
  2. {axio_tui-0.2.2 → axio_tui-0.3.2}/Makefile +1 -1
  3. {axio_tui-0.2.2 → axio_tui-0.3.2}/PKG-INFO +4 -1
  4. {axio_tui-0.2.2 → axio_tui-0.3.2}/pyproject.toml +6 -1
  5. {axio_tui-0.2.2 → axio_tui-0.3.2}/src/axio_tui/transport_registry.py +33 -3
  6. {axio_tui-0.2.2 → axio_tui-0.3.2}/uv.lock +1 -1
  7. {axio_tui-0.2.2 → axio_tui-0.3.2}/.github/workflows/publish.yml +0 -0
  8. {axio_tui-0.2.2 → axio_tui-0.3.2}/.github/workflows/tests.yml +0 -0
  9. {axio_tui-0.2.2 → axio_tui-0.3.2}/LICENSE +0 -0
  10. {axio_tui-0.2.2 → axio_tui-0.3.2}/README.md +0 -0
  11. {axio_tui-0.2.2 → axio_tui-0.3.2}/src/axio_tui/__init__.py +0 -0
  12. {axio_tui-0.2.2 → axio_tui-0.3.2}/src/axio_tui/__main__.py +0 -0
  13. {axio_tui-0.2.2 → axio_tui-0.3.2}/src/axio_tui/app.py +0 -0
  14. {axio_tui-0.2.2 → axio_tui-0.3.2}/src/axio_tui/args.py +0 -0
  15. {axio_tui-0.2.2 → axio_tui-0.3.2}/src/axio_tui/plugin.py +0 -0
  16. {axio_tui-0.2.2 → axio_tui-0.3.2}/src/axio_tui/prompt.py +0 -0
  17. {axio_tui-0.2.2 → axio_tui-0.3.2}/src/axio_tui/screens.py +0 -0
  18. {axio_tui-0.2.2 → axio_tui-0.3.2}/src/axio_tui/sqlite_context.py +0 -0
  19. {axio_tui-0.2.2 → axio_tui-0.3.2}/src/axio_tui/tools.py +0 -0
  20. {axio_tui-0.2.2 → axio_tui-0.3.2}/tests/conftest.py +0 -0
  21. {axio_tui-0.2.2 → axio_tui-0.3.2}/tests/test_guard_screens.py +0 -0
  22. {axio_tui-0.2.2 → axio_tui-0.3.2}/tests/test_nav.py +0 -0
  23. {axio_tui-0.2.2 → axio_tui-0.3.2}/tests/test_plugin.py +0 -0
  24. {axio_tui-0.2.2 → axio_tui-0.3.2}/tests/test_plugin_toggle.py +0 -0
  25. {axio_tui-0.2.2 → axio_tui-0.3.2}/tests/test_sqlite_context.py +0 -0
  26. {axio_tui-0.2.2 → axio_tui-0.3.2}/tests/test_tool_status_widget.py +0 -0
  27. {axio_tui-0.2.2 → axio_tui-0.3.2}/tests/test_tools.py +0 -0
  28. {axio_tui-0.2.2 → axio_tui-0.3.2}/tests/test_transport_registry.py +0 -0
@@ -0,0 +1,12 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ *.pyo
5
+ .mypy_cache/
6
+ .ruff_cache/
7
+ .pytest_cache/
8
+ *.egg-info/
9
+ dist/
10
+ build/
11
+ _build/
12
+ .DS_Store
@@ -8,7 +8,7 @@ lint:
8
8
  uv run ruff check src/ tests/
9
9
 
10
10
  typecheck:
11
- uv run mypy src/
11
+ uv run mypy src/ tests/
12
12
 
13
13
  test:
14
14
  uv run pytest tests/ -v
@@ -1,9 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: axio-tui
3
- Version: 0.2.2
3
+ Version: 0.3.2
4
4
  Summary: Textual TUI application for Axio
5
+ Project-URL: Homepage, https://github.com/axio-agent/axio-tui
6
+ Project-URL: Repository, https://github.com/axio-agent/axio-tui
5
7
  License: MIT
6
8
  License-File: LICENSE
9
+ Keywords: agent,ai,chat,llm,terminal,textual,tui
7
10
  Requires-Python: >=3.12
8
11
  Requires-Dist: aiosqlite>=0.20
9
12
  Requires-Dist: argclass>=1.6
@@ -1,10 +1,11 @@
1
1
  [project]
2
2
  name = "axio-tui"
3
- version = "0.2.2"
3
+ version = "0.3.2"
4
4
  description = "Textual TUI application for Axio"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
7
7
  license = {text = "MIT"}
8
+ keywords = ["llm", "agent", "ai", "tui", "textual", "terminal", "chat"]
8
9
  dependencies = [
9
10
  "axio",
10
11
  "textual>=2.1.0",
@@ -34,6 +35,10 @@ vision = "axio_tui.tools:VisionAnalyze"
34
35
  [project.scripts]
35
36
  axio = "axio_tui.__main__:main"
36
37
 
38
+
39
+ [project.urls]
40
+ Homepage = "https://github.com/axio-agent/axio-tui"
41
+ Repository = "https://github.com/axio-agent/axio-tui"
37
42
  [build-system]
38
43
  requires = ["hatchling"]
39
44
  build-backend = "hatchling.build"
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import asyncio
5
6
  import logging
6
7
  import os
7
8
  from dataclasses import dataclass, field
@@ -67,7 +68,7 @@ class TransportRegistry:
67
68
  # Determine API key: saved settings first, env second
68
69
  env_key = os.environ.get(meta.api_key_env, "") if meta.api_key_env else ""
69
70
  api_key = saved.get("api_key", "") or env_key
70
- if not api_key:
71
+ if not api_key and meta.api_key_env:
71
72
  logger.info("Transport %r: no API key (env %s), skipping", name, meta.api_key_env)
72
73
  continue
73
74
 
@@ -84,6 +85,19 @@ class TransportRegistry:
84
85
  logger.warning("Transport %r: fetch_models failed", name, exc_info=True)
85
86
  self._transports[name] = transport
86
87
 
88
+ # Bulk-load providers from hub screens that expose load_config()
89
+ # (e.g. CustomHubScreen for openai-custom.* providers)
90
+ for screen_name, screen_cls in self._screens.items():
91
+ load = getattr(screen_cls, "load_config", None)
92
+ if not callable(load):
93
+ continue
94
+ try:
95
+ providers = await asyncio.to_thread(load, session)
96
+ for t in providers:
97
+ self.register_dynamic(f"{screen_name}.{t.name}", t)
98
+ except Exception:
99
+ logger.warning("Failed to bulk-load providers from %r", screen_name, exc_info=True)
100
+
87
101
  @staticmethod
88
102
  async def _load_settings(config: ProjectConfig, name: str) -> dict[str, str]:
89
103
  """Load saved transport settings from config, stripping the key prefix."""
@@ -114,7 +128,7 @@ class TransportRegistry:
114
128
  meta = self._metas[name]
115
129
  env_key = os.environ.get(meta.api_key_env, "") if meta.api_key_env else ""
116
130
  api_key = settings.get("api_key", "") or env_key
117
- if api_key:
131
+ if api_key or not meta.api_key_env:
118
132
  kwargs: dict[str, Any] = {"api_key": api_key, "session": self._session}
119
133
  for k, v in settings.items():
120
134
  if k != "api_key" and v:
@@ -161,16 +175,32 @@ class TransportRegistry:
161
175
  cls = self._classes[name]
162
176
  kwargs: dict[str, Any] = {
163
177
  "api_key": src.api_key,
178
+ "base_url": src.base_url,
164
179
  "model": model,
165
180
  "models": src.models,
166
181
  "session": src.session,
167
182
  }
168
- # Apply saved settings (e.g. base_url) that aren't already set
183
+ # Apply saved settings that aren't already set
169
184
  for k, v in self._saved.get(name, {}).items():
170
185
  if k not in kwargs and v:
171
186
  kwargs[k] = v
172
187
  return cls(**kwargs)
173
188
 
189
+ def register_dynamic(self, name: str, instance: Any) -> None:
190
+ """Register a dynamically-created transport instance (e.g. custom providers)."""
191
+ self._transports[name] = instance
192
+ self._classes[name] = type(instance)
193
+ self._metas[name] = type(instance).META
194
+
195
+ def unregister_by_prefix(self, prefix: str) -> None:
196
+ """Remove all transports whose names start with *prefix*."""
197
+ names = [n for n in list(self._transports) if n.startswith(prefix)]
198
+ for n in names:
199
+ del self._transports[n]
200
+ self._classes.pop(n, None)
201
+ self._metas.pop(n, None)
202
+ self._saved.pop(n, None)
203
+
174
204
  def resolve(self, config_value: str) -> RoleBinding | None:
175
205
  """Parse a config value like ``"nebius:model_id"`` into a RoleBinding.
176
206
 
@@ -248,7 +248,7 @@ wheels = [
248
248
 
249
249
  [[package]]
250
250
  name = "axio-tui"
251
- version = "0.2.2"
251
+ version = "0.2.3"
252
252
  source = { editable = "." }
253
253
  dependencies = [
254
254
  { name = "aiosqlite" },
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