astromesh-cli 0.3.0__tar.gz → 0.4.0__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 (39) hide show
  1. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/.gitignore +9 -0
  2. astromesh_cli-0.4.0/CHANGELOG.md +46 -0
  3. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/PKG-INFO +3 -3
  4. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/astromesh_cli/__init__.py +1 -1
  5. astromesh_cli-0.4.0/astromesh_cli/commands/eval.py +17 -0
  6. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/astromesh_cli/commands/new.py +7 -2
  7. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/astromesh_cli/commands/run.py +42 -9
  8. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/astromesh_cli/main.py +5 -2
  9. astromesh_cli-0.4.0/astromesh_cli/templates/agent.yaml.j2 +47 -0
  10. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/pyproject.toml +2 -2
  11. astromesh_cli-0.4.0/tests/test_core070.py +73 -0
  12. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/uv.lock +18 -4
  13. astromesh_cli-0.3.0/CHANGELOG.md +0 -23
  14. astromesh_cli-0.3.0/astromesh_cli/templates/agent.yaml.j2 +0 -29
  15. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/README.md +0 -0
  16. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/astromesh_cli/client.py +0 -0
  17. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/astromesh_cli/commands/__init__.py +0 -0
  18. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/astromesh_cli/commands/agents.py +0 -0
  19. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/astromesh_cli/commands/ask.py +0 -0
  20. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/astromesh_cli/commands/dev.py +0 -0
  21. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/astromesh_cli/commands/doctor.py +0 -0
  22. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/astromesh_cli/commands/mesh.py +0 -0
  23. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/astromesh_cli/commands/metrics.py +0 -0
  24. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/astromesh_cli/commands/peers.py +0 -0
  25. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/astromesh_cli/commands/providers.py +0 -0
  26. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/astromesh_cli/commands/services.py +0 -0
  27. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/astromesh_cli/commands/status.py +0 -0
  28. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/astromesh_cli/commands/tools.py +0 -0
  29. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/astromesh_cli/commands/traces.py +0 -0
  30. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/astromesh_cli/output.py +0 -0
  31. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/astromesh_cli/templates/tool.py.j2 +0 -0
  32. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/astromesh_cli/templates/workflow.yaml.j2 +0 -0
  33. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/tests/conftest.py +0 -0
  34. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/tests/test_ask.py +0 -0
  35. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/tests/test_client.py +0 -0
  36. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/tests/test_main.py +0 -0
  37. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/tests/test_output.py +0 -0
  38. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/tests/test_run_usage.py +0 -0
  39. {astromesh_cli-0.3.0 → astromesh_cli-0.4.0}/tests/test_version.py +0 -0
@@ -43,3 +43,12 @@ docs-site/.astro/
43
43
  # SDD scratch
44
44
  .superpowers/
45
45
  config/connections.yaml
46
+
47
+ # Worktrees de sesiones de Claude Code. Son repos git EMBEBIDOS: un `git add -A`
48
+ # se los lleva adentro de un commit, y ya pasó — en astromesh-herald entró uno
49
+ # dentro de un commit de release. Se ignora SÓLO `worktrees/`: este repo SÍ
50
+ # versiona `.claude/skills/` y sus comandos, y un ignore amplio se los llevaría.
51
+ .claude/worktrees/
52
+
53
+ # macOS
54
+ .DS_Store
@@ -0,0 +1,46 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.4.0] - 2026-10-16
11
+
12
+ ### Added
13
+ - `astromeshctl eval <config_dir> [archivos...] [--out reporte.json]`: corre los `kind: Eval` con
14
+ el runner del core (`astromesh.evals`, el mismo de `astromesh-eval`). Exit 0 si todos cumplen,
15
+ 1 si alguno queda bajo umbral, 2 por error de carga (un eval inválido no corre ningún caso).
16
+ - `run` muestra un panel **BLOCKED by guardrail** con dirección, regla y motivo cuando el core
17
+ 0.70 bloquea la corrida (que vuelve HTTP 200 con el rechazo como `answer`), y una tabla con las
18
+ `propuestas` de las tools `mode: propose`. La tabla por modelo suma la columna `Cached`
19
+ (`tokens_cached`).
20
+ - `new agent --context-window N`; la plantilla trae `guardrails` válidos para el core 0.70
21
+ (`pii_detection` en entrada y salida, `max_length`, `cost_limit` 4000), `context_window`
22
+ (comentado si no se pasa) y un `knowledge.bundle` comentado. Un test la carga con
23
+ `AgentRuntime` sin warnings de guardrails.
24
+
25
+ ### Changed
26
+ - Piso del core `astromesh>=0.70.0`.
27
+
28
+ ## [0.3.1] - 2026-09-27
29
+
30
+ ### Changed
31
+ - Bumped the `astromesh` core dependency floor to `>=0.59.0` (`pyproject.toml`). No CLI code changes.
32
+
33
+ ## [0.2.1] - 2026-08-10
34
+
35
+ ### Changed
36
+ - Bumped the `astromesh` core dependency floor to `>=0.40.0` (`pyproject.toml`).
37
+
38
+ ## [0.2.0] - 2026-07-27
39
+
40
+ ### Added
41
+ - `run` now surfaces the per-model consumption breakdown (`usage.by_model`, astromesh core v0.36.0): a "Por modelo" table with provider / model / role / calls / tokens in-out / cost.
42
+ - `version` now also reports the installed astromesh **core** version (via `importlib.metadata`), not just the CLI's own.
43
+
44
+ ### Changed
45
+ - Bumped the astromesh core dependency pin to `>=0.36.0`.
46
+ - `run` and `ask` now read the real run-response contract (`answer` + `usage`) instead of the stale `response`/`tokens_used` field names, via a shared `_format_run_output` helper; `trace_id` is read from the nested `trace`. `ask` previously rendered an empty body against core ≥ the version that introduced `answer`.
@@ -1,10 +1,10 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: astromesh-cli
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: CLI tool for managing Astromesh nodes and clusters
5
5
  License: Apache-2.0
6
6
  Requires-Python: >=3.12
7
- Requires-Dist: astromesh>=0.40.0
7
+ Requires-Dist: astromesh>=0.70.0
8
8
  Requires-Dist: jinja2>=3.1.0
9
9
  Requires-Dist: rich>=13.0.0
10
10
  Requires-Dist: typer>=0.12.0
@@ -1,3 +1,3 @@
1
1
  """Astromesh CLI — manage Astromesh nodes and clusters."""
2
2
 
3
- __version__ = "0.3.0"
3
+ __version__ = "0.4.0"
@@ -0,0 +1,17 @@
1
+ """astromeshctl eval — run `kind: Eval` files with core's eval runner (`astromesh-eval`)."""
2
+
3
+ import typer
4
+
5
+
6
+ def eval_command(
7
+ config_dir: str = typer.Argument(..., help="Config tree with the agents under test"),
8
+ files: list[str] | None = typer.Argument( # noqa: B008
9
+ None, help="*.eval.yaml files (default: <config_dir>/evals/*.eval.yaml)"
10
+ ),
11
+ out: str | None = typer.Option(None, "--out", help="Write the JSON report here"),
12
+ ) -> None:
13
+ """Run agent evals locally. Exit 0 all pass, 1 below threshold, 2 load error."""
14
+ from astromesh.evals.__main__ import main
15
+
16
+ argv = [config_dir, *(files or [])] + (["--out", out] if out else [])
17
+ raise typer.Exit(code=main(argv))
@@ -1,7 +1,6 @@
1
1
  """astromeshctl new — Scaffold new agents, workflows, and tools."""
2
2
 
3
3
  from pathlib import Path
4
- from typing import Optional
5
4
 
6
5
  import typer
7
6
  from jinja2 import Environment, FileSystemLoader
@@ -27,6 +26,7 @@ def render_agent_template(
27
26
  temperature: float = 0.3,
28
27
  max_tokens: int = 2048,
29
28
  description: str = "",
29
+ context_window: int | None = None,
30
30
  ) -> str:
31
31
  """Render an agent YAML template."""
32
32
  env = _get_jinja_env()
@@ -42,6 +42,7 @@ def render_agent_template(
42
42
  tools=tools or [],
43
43
  temperature=temperature,
44
44
  max_tokens=max_tokens,
45
+ context_window=context_window,
45
46
  )
46
47
 
47
48
 
@@ -80,7 +81,10 @@ def new_agent(
80
81
  provider: str = typer.Option("ollama", "--provider", help="LLM provider"),
81
82
  model: str = typer.Option("llama3.1:8b", "--model", help="Model name"),
82
83
  orchestration: str = typer.Option("react", "--orchestration", help="Orchestration pattern"),
83
- tools: Optional[list[str]] = typer.Option(None, "--tools", help="Tools to include"),
84
+ tools: list[str] | None = typer.Option(None, "--tools", help="Tools to include"), # noqa: B008
85
+ context_window: int | None = typer.Option(
86
+ None, "--context-window", help="Model context window in tokens (sizes the history)"
87
+ ),
84
88
  output_dir: str = typer.Option("./config/agents", "--output-dir", help="Output directory"),
85
89
  force: bool = typer.Option(False, "--force", help="Overwrite existing file"),
86
90
  ) -> None:
@@ -99,6 +103,7 @@ def new_agent(
99
103
  model=model,
100
104
  orchestration_pattern=orchestration,
101
105
  tools=tools,
106
+ context_window=context_window,
102
107
  )
103
108
  dest.write_text(content)
104
109
  console.print(
@@ -2,7 +2,6 @@
2
2
 
3
3
  import json
4
4
  import uuid
5
- from typing import Optional
6
5
 
7
6
  import typer
8
7
  from rich.panel import Panel
@@ -36,16 +35,44 @@ def _format_run_output(data: dict) -> tuple[str, str, list[dict]]:
36
35
  return text, subtitle, rows
37
36
 
38
37
 
38
+ def _print_guardrail_and_propuestas(data: dict) -> None:
39
+ """Core 0.70: a blocked run is HTTP 200 with the refusal as `answer`; say so."""
40
+ if data.get("blocked"):
41
+ g = data.get("guardrail") or {}
42
+ console.print(
43
+ Panel(
44
+ f"{g.get('direction', '?')} rule [bold]{g.get('rule', '?')}[/bold]: "
45
+ f"{g.get('reason', '')}",
46
+ title="[red]BLOCKED by guardrail[/red]",
47
+ border_style="red",
48
+ )
49
+ )
50
+ propuestas = [p for p in data.get("propuestas") or [] if isinstance(p, dict)]
51
+ if propuestas:
52
+ table = Table(title="Propuestas (sin ejecutar)", show_header=True)
53
+ for col in ("Tool", "Tipo", "Destino", "Operación", "Argumentos"):
54
+ table.add_column(col, style="cyan" if col == "Tool" else None)
55
+ for p in propuestas:
56
+ table.add_row(
57
+ str(p.get("tool", "")),
58
+ str(p.get("tipo", "")),
59
+ str(p.get("destino", "")),
60
+ str(p.get("operacion", "")),
61
+ json.dumps(p.get("argumentos", {}), ensure_ascii=False),
62
+ )
63
+ console.print(table)
64
+
65
+
39
66
  def run_command(
40
67
  name: str = typer.Argument(..., help="Agent or workflow name to run"),
41
68
  query: str = typer.Argument("", help="Query to send to the agent"),
42
- session: Optional[str] = typer.Option(
69
+ session: str | None = typer.Option(
43
70
  None, "--session", help="Session ID (auto-generated if not set)"
44
71
  ),
45
72
  json_output: bool = typer.Option(False, "--json", help="Output raw JSON response"),
46
73
  timeout: float = typer.Option(60.0, "--timeout", help="Request timeout in seconds"),
47
74
  workflow: bool = typer.Option(False, "--workflow", help="Run as workflow instead of agent"),
48
- input_data: Optional[str] = typer.Option(None, "--input", help="Workflow input data (JSON)"),
75
+ input_data: str | None = typer.Option(None, "--input", help="Workflow input data (JSON)"),
49
76
  ) -> None:
50
77
  """Execute an agent with a query or run a workflow."""
51
78
  if workflow:
@@ -64,7 +91,7 @@ def run_command(
64
91
  json={"query": query, "session_id": session_id},
65
92
  timeout=timeout,
66
93
  )
67
- except Exception as e:
94
+ except Exception as e: # noqa: BLE001
68
95
  print_error(f"Failed to run agent '{name}': {e}")
69
96
  raise typer.Exit(code=1)
70
97
 
@@ -78,18 +105,24 @@ def run_command(
78
105
  text,
79
106
  title=f"[cyan]{name}[/cyan]",
80
107
  subtitle=subtitle,
81
- border_style="blue",
108
+ border_style="red" if data.get("blocked") else "blue",
82
109
  )
83
110
  )
111
+ _print_guardrail_and_propuestas(data)
84
112
  if by_model_rows:
85
113
  table = Table(title="Por modelo", show_header=True)
86
- for col in ("Provider", "Model", "Role", "Calls", "In", "Out", "Cost"):
114
+ for col in ("Provider", "Model", "Role", "Calls", "In", "Cached", "Out", "Cost"):
87
115
  table.add_column(col, style="cyan" if col == "Provider" else None)
88
116
  for r in by_model_rows:
89
117
  cost = r.get("cost", 0.0) or 0.0
90
118
  table.add_row(
91
- r.get("provider", ""), r.get("model", ""), r.get("role", ""),
92
- str(r.get("calls", 0)), str(r.get("tokens_in", 0)), str(r.get("tokens_out", 0)),
119
+ r.get("provider", ""),
120
+ r.get("model", ""),
121
+ r.get("role", ""),
122
+ str(r.get("calls", 0)),
123
+ str(r.get("tokens_in", 0)),
124
+ str(r.get("tokens_cached", 0)),
125
+ str(r.get("tokens_out", 0)),
93
126
  f"${cost:.4f}" if cost else "—",
94
127
  )
95
128
  console.print(table)
@@ -115,7 +148,7 @@ def _run_workflow(
115
148
  json=payload,
116
149
  timeout=timeout,
117
150
  )
118
- except Exception as e:
151
+ except Exception as e: # noqa: BLE001
119
152
  print_error(f"Failed to run workflow '{name}': {e}")
120
153
  raise typer.Exit(code=1)
121
154
 
@@ -21,6 +21,7 @@ from astromesh_cli.commands import (
21
21
  tools,
22
22
  traces,
23
23
  )
24
+ from astromesh_cli.commands import eval as eval_cmd
24
25
 
25
26
  app = typer.Typer(
26
27
  name="astromeshctl",
@@ -44,12 +45,14 @@ app.command("trace")(traces.trace_command)
44
45
  app.command("metrics")(metrics.metrics_command)
45
46
  app.command("cost")(metrics.cost_command)
46
47
  app.command("ask")(ask.ask_command)
48
+ app.command("eval")(eval_cmd.eval_command)
47
49
 
48
50
 
49
51
  @app.command()
50
52
  def version():
51
53
  """Show astromesh version."""
52
- from importlib.metadata import PackageNotFoundError, version as _pkg_version
54
+ from importlib.metadata import PackageNotFoundError
55
+ from importlib.metadata import version as _pkg_version
53
56
 
54
57
  typer.echo(f"astromesh-cli {__version__}")
55
58
  try:
@@ -70,7 +73,7 @@ def _load_plugins(app: typer.Typer) -> None:
70
73
  try:
71
74
  register_fn = ep.load()
72
75
  register_fn(app)
73
- except Exception as exc:
76
+ except Exception as exc: # noqa: BLE001
74
77
  typer.echo(f"Warning: failed to load plugin '{ep.name}': {exc}", err=True)
75
78
 
76
79
 
@@ -0,0 +1,47 @@
1
+ apiVersion: astromesh/v1
2
+ kind: Agent
3
+ metadata:
4
+ name: {{ name }}
5
+ spec:
6
+ identity:
7
+ display_name: "{{ display_name }}"
8
+ description: "{{ description }}"
9
+ model:
10
+ primary:
11
+ provider: {{ provider }}
12
+ model: {{ model }}
13
+ temperature: {{ temperature }}
14
+ max_tokens: {{ max_tokens }}
15
+ {% if context_window %}
16
+ context_window: {{ context_window }}
17
+ {% else %}
18
+ # Declare the model's window so the runtime sizes the history budget:
19
+ # context_window: 128000
20
+ {% endif %}
21
+ routing:
22
+ strategy: quality_first
23
+ prompts:
24
+ system: |
25
+ You are {{ display_name }}, a helpful AI assistant.
26
+ orchestration:
27
+ pattern: {{ orchestration_pattern }}
28
+ max_iterations: 10
29
+ timeout: 30
30
+ {% if tools %}
31
+ tools:
32
+ {% for tool in tools %}
33
+ - name: {{ tool }}
34
+ {% endfor %}
35
+ {% endif %}
36
+ guardrails:
37
+ input:
38
+ - type: pii_detection # redact (default) | block | warn
39
+ - type: max_length # truncate (default) | block
40
+ max_chars: 10000
41
+ output:
42
+ - type: pii_detection
43
+ - type: cost_limit # blocks the run past this many output tokens
44
+ max_tokens_per_turn: 4000
45
+ # An OKF knowledge bundle (a directory) adds the kb_index/kb_read/kb_find tools:
46
+ # knowledge:
47
+ # bundle: ./knowledge/{{ name }}
@@ -4,12 +4,12 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "astromesh-cli"
7
- version = "0.3.0"
7
+ version = "0.4.0"
8
8
  description = "CLI tool for managing Astromesh nodes and clusters"
9
9
  requires-python = ">=3.12"
10
10
  license = {text = "Apache-2.0"}
11
11
  dependencies = [
12
- "astromesh>=0.40.0",
12
+ "astromesh>=0.70.0",
13
13
  "typer>=0.12.0",
14
14
  "rich>=13.0.0",
15
15
  "jinja2>=3.1.0",
@@ -0,0 +1,73 @@
1
+ """CLI against core 0.70: blocked/propuestas in `run`, a template core loads, `eval`."""
2
+
3
+ import logging
4
+
5
+ import yaml
6
+ from typer.testing import CliRunner
7
+
8
+ from astromesh_cli.commands import run as run_mod
9
+ from astromesh_cli.commands.new import render_agent_template
10
+ from astromesh_cli.main import app
11
+
12
+ runner = CliRunner()
13
+
14
+
15
+ def test_run_shows_blocked_and_propuestas(monkeypatch):
16
+ body = {
17
+ "answer": "I can't help with that request.",
18
+ "blocked": True,
19
+ "guardrail": {"direction": "input", "rule": "pii_detection", "reason": "pii detected"},
20
+ "propuestas": [
21
+ {"tool": "erp_crear", "tipo": "api", "destino": "erp", "operacion": "crear"}
22
+ ],
23
+ "usage": {"tokens_in": 0, "tokens_out": 0, "by_model": []},
24
+ }
25
+ monkeypatch.setattr(run_mod, "api_post_with_timeout", lambda *a, **k: body)
26
+ result = runner.invoke(app, ["run", "support", "hi"])
27
+ assert result.exit_code == 0
28
+ assert "BLOCKED by guardrail" in result.stdout and "pii_detection" in result.stdout
29
+ assert "erp_crear" in result.stdout
30
+
31
+
32
+ async def test_agent_template_loads_in_core_without_warnings(tmp_path, caplog):
33
+ from astromesh.runtime.engine import AgentRuntime
34
+
35
+ (tmp_path / "agents").mkdir()
36
+ text = render_agent_template("bot", context_window=128000)
37
+ spec = yaml.safe_load(text)["spec"]
38
+ assert spec["model"]["primary"]["context_window"] == 128000
39
+ (tmp_path / "agents" / "bot.agent.yaml").write_text(text)
40
+ runtime = AgentRuntime(config_dir=str(tmp_path))
41
+ with caplog.at_level(logging.WARNING, logger="astromesh.core.guardrails"):
42
+ await runtime.bootstrap()
43
+ assert "bot" in runtime._agents, runtime._agent_errors
44
+ assert not caplog.records
45
+ guard = runtime._agents["bot"]._guardrails
46
+ assert guard.has_input and guard.has_output and guard.cost_limit["max_tokens"] == 4000
47
+
48
+
49
+ def test_template_without_context_window_leaves_it_commented():
50
+ assert (
51
+ "context_window"
52
+ not in yaml.safe_load(render_agent_template("bot"))["spec"]["model"]["primary"]
53
+ )
54
+
55
+
56
+ def test_eval_forwards_to_core_runner(monkeypatch):
57
+ import astromesh.evals.__main__ as core_eval
58
+
59
+ seen = {}
60
+
61
+ def fake_main(argv):
62
+ seen["argv"] = argv
63
+ return 1
64
+
65
+ monkeypatch.setattr(core_eval, "main", fake_main)
66
+ result = runner.invoke(app, ["eval", "cfg", "a.eval.yaml", "--out", "r.json"])
67
+ assert result.exit_code == 1
68
+ assert seen["argv"] == ["cfg", "a.eval.yaml", "--out", "r.json"]
69
+
70
+
71
+ def test_eval_without_evals_is_a_load_error(tmp_path):
72
+ result = runner.invoke(app, ["eval", str(tmp_path)])
73
+ assert result.exit_code == 2
@@ -35,9 +35,10 @@ wheels = [
35
35
 
36
36
  [[package]]
37
37
  name = "astromesh"
38
- version = "0.40.0"
38
+ version = "0.70.0"
39
39
  source = { editable = "../" }
40
40
  dependencies = [
41
+ { name = "astromesh-okf" },
41
42
  { name = "fastapi" },
42
43
  { name = "httpx" },
43
44
  { name = "jinja2" },
@@ -52,6 +53,7 @@ requires-dist = [
52
53
  { name = "aiosqlite", marker = "extra == 'sqlite'", specifier = ">=0.20.0" },
53
54
  { name = "astromesh", extras = ["redis", "postgres", "sqlite", "chromadb", "qdrant", "faiss", "embeddings", "onnx", "observability", "mcp", "mesh", "litellm", "centinela", "glyph"], marker = "extra == 'all'" },
54
55
  { name = "astromesh-glyph", marker = "extra == 'glyph'", editable = "../astromesh-glyph" },
56
+ { name = "astromesh-okf", specifier = ">=0.1,<0.2" },
55
57
  { name = "asyncpg", marker = "extra == 'postgres'", specifier = ">=0.30.0" },
56
58
  { name = "chromadb", marker = "extra == 'chromadb'", specifier = ">=0.5.0" },
57
59
  { name = "faiss-cpu", marker = "extra == 'faiss'", specifier = ">=1.8.0" },
@@ -60,7 +62,7 @@ requires-dist = [
60
62
  { name = "huggingface-hub", marker = "extra == 'centinela'", specifier = ">=0.24" },
61
63
  { name = "jinja2", specifier = ">=3.1.0" },
62
64
  { name = "litellm", marker = "extra == 'litellm'", specifier = ">=1.50.0" },
63
- { name = "mcp", marker = "extra == 'mcp'", specifier = ">=1.0.0" },
65
+ { name = "mcp", marker = "extra == 'mcp'", specifier = ">=1.28.1,<2" },
64
66
  { name = "onnxruntime", marker = "extra == 'onnx'", specifier = ">=1.19.0" },
65
67
  { name = "opentelemetry-api", marker = "extra == 'observability'", specifier = ">=1.27.0" },
66
68
  { name = "opentelemetry-exporter-otlp", marker = "extra == 'observability'", specifier = ">=1.27.0" },
@@ -68,7 +70,7 @@ requires-dist = [
68
70
  { name = "prometheus-client", marker = "extra == 'observability'", specifier = ">=0.21.0" },
69
71
  { name = "psutil", specifier = ">=5.9.0" },
70
72
  { name = "psutil", marker = "extra == 'mesh'", specifier = ">=5.9.0" },
71
- { name = "pydantic", specifier = ">=2.9.0" },
73
+ { name = "pydantic", specifier = ">=2.9.0,<3" },
72
74
  { name = "pyyaml", specifier = ">=6.0" },
73
75
  { name = "qdrant-client", marker = "extra == 'qdrant'", specifier = ">=1.11.0" },
74
76
  { name = "redis", extras = ["hiredis"], marker = "extra == 'redis'", specifier = ">=5.0.0" },
@@ -95,7 +97,7 @@ dev = [
95
97
 
96
98
  [[package]]
97
99
  name = "astromesh-cli"
98
- version = "0.3.0"
100
+ version = "0.4.0"
99
101
  source = { editable = "." }
100
102
  dependencies = [
101
103
  { name = "astromesh" },
@@ -137,6 +139,18 @@ requires-dist = [
137
139
  ]
138
140
  provides-extras = ["dev", "test", "all"]
139
141
 
142
+ [[package]]
143
+ name = "astromesh-okf"
144
+ version = "0.1.0"
145
+ source = { registry = "https://pypi.org/simple" }
146
+ dependencies = [
147
+ { name = "pyyaml" },
148
+ ]
149
+ sdist = { url = "https://files.pythonhosted.org/packages/a5/33/ccf8f70e6cd22021dff6e11cba7c34b3d25d504675a1a417f18bc7c37730/astromesh_okf-0.1.0.tar.gz", hash = "sha256:acb932dc0a6969a77b52588e3572c147b0b69f5136860818c1b83474b503c7cf", size = 51340, upload-time = "2026-10-14T02:03:58.256Z" }
150
+ wheels = [
151
+ { url = "https://files.pythonhosted.org/packages/e9/9d/cf840f718086009198c763516beec46abaf5d2346ea2afde34c9c6be94b3/astromesh_okf-0.1.0-py3-none-any.whl", hash = "sha256:8be5fa9d80cbd331decc02f2f28b3b614d79f5fc7d7967ed15cb1f0aa20b2bab", size = 28989, upload-time = "2026-10-14T02:03:57.036Z" },
152
+ ]
153
+
140
154
  [[package]]
141
155
  name = "certifi"
142
156
  version = "2026.7.22"
@@ -1,23 +0,0 @@
1
- # Changelog
2
-
3
- All notable changes to this project will be documented in this file.
4
-
5
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
- and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
-
8
- ## [Unreleased]
9
-
10
- ## [0.2.1] - 2026-08-10
11
-
12
- ### Changed
13
- - Bumped the `astromesh` core dependency floor to `>=0.40.0` (`pyproject.toml`).
14
-
15
- ## [0.2.0] - 2026-07-27
16
-
17
- ### Added
18
- - `run` now surfaces the per-model consumption breakdown (`usage.by_model`, astromesh core v0.36.0): a "Por modelo" table with provider / model / role / calls / tokens in-out / cost.
19
- - `version` now also reports the installed astromesh **core** version (via `importlib.metadata`), not just the CLI's own.
20
-
21
- ### Changed
22
- - Bumped the astromesh core dependency pin to `>=0.36.0`.
23
- - `run` and `ask` now read the real run-response contract (`answer` + `usage`) instead of the stale `response`/`tokens_used` field names, via a shared `_format_run_output` helper; `trace_id` is read from the nested `trace`. `ask` previously rendered an empty body against core ≥ the version that introduced `answer`.
@@ -1,29 +0,0 @@
1
- apiVersion: astromesh/v1
2
- kind: Agent
3
- metadata:
4
- name: {{ name }}
5
- spec:
6
- identity:
7
- display_name: "{{ display_name }}"
8
- description: "{{ description }}"
9
- model:
10
- primary:
11
- provider: {{ provider }}
12
- model: {{ model }}
13
- temperature: {{ temperature }}
14
- max_tokens: {{ max_tokens }}
15
- routing:
16
- strategy: quality_first
17
- prompts:
18
- system: |
19
- You are {{ display_name }}, a helpful AI assistant.
20
- orchestration:
21
- pattern: {{ orchestration_pattern }}
22
- max_iterations: 10
23
- timeout: 30
24
- {% if tools %}
25
- tools:
26
- {% for tool in tools %}
27
- - name: {{ tool }}
28
- {% endfor %}
29
- {% endif %}
File without changes