agent-devkit 0.3.0 → 0.3.2
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.
- package/README.md +25 -11
- package/package.json +1 -1
- package/runtime/README.md +20 -10
- package/runtime/cli/README.md +14 -6
- package/runtime/cli/aikit/__init__.py +1 -1
- package/runtime/cli/aikit/aliases.py +181 -0
- package/runtime/cli/aikit/app_home.py +1 -0
- package/runtime/cli/aikit/cli_dispatch.py +5 -1
- package/runtime/cli/aikit/cli_parser.py +3 -2
- package/runtime/cli/aikit/embedded_mini_brain.py +351 -0
- package/runtime/cli/aikit/human_output.py +32 -0
- package/runtime/cli/aikit/interactive_wizard.py +181 -13
- package/runtime/cli/aikit/llm.py +28 -2
- package/runtime/cli/aikit/local_llm.py +19 -4
- package/runtime/cli/aikit/local_llm_operator.py +15 -5
- package/runtime/cli/aikit/mini_brain.py +56 -44
- package/runtime/cli/aikit/model_router.py +42 -9
- package/runtime/cli/aikit/natural_prompt_runtime.py +86 -2
- package/runtime/cli/aikit/onboarding.py +3 -3
- package/runtime/cli/aikit/personality.py +3 -0
- package/runtime/cli/aikit/review_gate.py +14 -2
- package/runtime/models/qwen2.5-0.5b-instruct/manifest.json +30 -0
- package/runtime/scripts/release-catalog-snapshot.json +1 -1
package/runtime/cli/aikit/llm.py
CHANGED
|
@@ -14,6 +14,14 @@ from pathlib import Path
|
|
|
14
14
|
from typing import Any
|
|
15
15
|
|
|
16
16
|
from cli.aikit.app_home import app_home, config_path as app_config_path, ensure_app_home
|
|
17
|
+
from cli.aikit.embedded_mini_brain import (
|
|
18
|
+
EMBEDDED_BACKEND_ID,
|
|
19
|
+
EMBEDDED_MODEL_ID,
|
|
20
|
+
EmbeddedMiniBrainError,
|
|
21
|
+
embedded_backend_config,
|
|
22
|
+
embedded_backend_doctor,
|
|
23
|
+
invoke_embedded_mini_brain,
|
|
24
|
+
)
|
|
17
25
|
from cli.aikit.identity import host_cli_prompt, identity_system_prompt
|
|
18
26
|
|
|
19
27
|
|
|
@@ -33,6 +41,14 @@ class LlmBackend:
|
|
|
33
41
|
|
|
34
42
|
|
|
35
43
|
BACKENDS: dict[str, LlmBackend] = {
|
|
44
|
+
EMBEDDED_BACKEND_ID: LlmBackend(
|
|
45
|
+
id=EMBEDDED_BACKEND_ID,
|
|
46
|
+
display_name="Embedded mini-brain",
|
|
47
|
+
kind="embedded-local",
|
|
48
|
+
auth="none",
|
|
49
|
+
default_model=EMBEDDED_MODEL_ID,
|
|
50
|
+
notes="Uses the Agent DevKit embedded mini-brain for setup, onboarding and low-risk conversation.",
|
|
51
|
+
),
|
|
36
52
|
"openai": LlmBackend(
|
|
37
53
|
id="openai",
|
|
38
54
|
display_name="OpenAI API",
|
|
@@ -100,7 +116,7 @@ BACKENDS: dict[str, LlmBackend] = {
|
|
|
100
116
|
|
|
101
117
|
ENV_VAR_NAME_PATTERN = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
|
|
102
118
|
DEFAULT_AGENT_TIMEOUT_SECONDS = 120
|
|
103
|
-
DEFAULT_FALLBACK_ORDER = ("claude-code", "codex-cli", "openai", "anthropic", "openrouter", "ollama")
|
|
119
|
+
DEFAULT_FALLBACK_ORDER = ("claude-code", "codex-cli", "openai", "anthropic", "openrouter", "ollama", EMBEDDED_BACKEND_ID)
|
|
104
120
|
|
|
105
121
|
|
|
106
122
|
def config_home() -> Path:
|
|
@@ -323,6 +339,8 @@ def normalize_backend_order(order: str | list[str] | tuple[str, ...]) -> list[st
|
|
|
323
339
|
|
|
324
340
|
|
|
325
341
|
def default_backend_config(backend: LlmBackend) -> dict[str, Any]:
|
|
342
|
+
if backend.id == EMBEDDED_BACKEND_ID:
|
|
343
|
+
return embedded_backend_config()
|
|
326
344
|
entry: dict[str, Any] = {"kind": backend.kind, "auth": backend.auth}
|
|
327
345
|
if backend.auth == "api-key-env":
|
|
328
346
|
entry["api_key_ref"] = f"env:{backend.api_key_env}"
|
|
@@ -346,7 +364,8 @@ def doctor_backends(backend_id: str | None = None) -> dict[str, Any]:
|
|
|
346
364
|
|
|
347
365
|
checks = [doctor_backend(BACKENDS[item], config) for item in ids]
|
|
348
366
|
status = "ok"
|
|
349
|
-
|
|
367
|
+
missing_statuses = {"missing", "not-installed", "dependency-missing", "invalid-model"}
|
|
368
|
+
if any(item["status"] in missing_statuses for item in checks):
|
|
350
369
|
status = "partial" if not backend_id else "missing"
|
|
351
370
|
if any(item["status"] == "error" for item in checks):
|
|
352
371
|
status = "error"
|
|
@@ -361,6 +380,8 @@ def doctor_backends(backend_id: str | None = None) -> dict[str, Any]:
|
|
|
361
380
|
|
|
362
381
|
|
|
363
382
|
def doctor_backend(backend: LlmBackend, config: dict[str, Any]) -> dict[str, Any]:
|
|
383
|
+
if backend.id == EMBEDDED_BACKEND_ID:
|
|
384
|
+
return embedded_backend_doctor()
|
|
364
385
|
configured = config.get("llm", {}).get("backends", {}).get(backend.id, {})
|
|
365
386
|
if not isinstance(configured, dict):
|
|
366
387
|
configured = {}
|
|
@@ -616,6 +637,11 @@ class LlmPolicyError(LlmInvocationError):
|
|
|
616
637
|
def invoke_resolved_backend(backend: dict[str, Any], prompt: str, *, public_name: str = "Agent DevKit") -> str:
|
|
617
638
|
kind = backend.get("kind")
|
|
618
639
|
backend_id = backend.get("id")
|
|
640
|
+
if kind == "embedded-local" and backend_id == EMBEDDED_BACKEND_ID:
|
|
641
|
+
try:
|
|
642
|
+
return invoke_embedded_mini_brain(prompt, public_name=public_name)
|
|
643
|
+
except EmbeddedMiniBrainError as exc:
|
|
644
|
+
raise LlmInvocationError(str(exc)) from exc
|
|
619
645
|
if kind == "openai-compatible":
|
|
620
646
|
return invoke_openai_compatible(backend, prompt, public_name=public_name)
|
|
621
647
|
if kind == "anthropic":
|
|
@@ -6,6 +6,7 @@ import shutil
|
|
|
6
6
|
import subprocess
|
|
7
7
|
from typing import Any
|
|
8
8
|
|
|
9
|
+
from cli.aikit.embedded_mini_brain import EMBEDDED_BACKEND_ID, EMBEDDED_MODEL_ID, embedded_mini_brain_status
|
|
9
10
|
from cli.aikit.mini_brain import DEFAULT_OLLAMA_MODEL, mini_brain_contract
|
|
10
11
|
from cli.aikit.model_router import build_model_plan
|
|
11
12
|
from cli.aikit.ollama import ollama_models, ollama_pull, ollama_status
|
|
@@ -28,7 +29,9 @@ def local_llm_list() -> dict[str, Any]:
|
|
|
28
29
|
"kind": "local-llm",
|
|
29
30
|
"schema_version": LOCAL_LLM_SCHEMA_VERSION,
|
|
30
31
|
"status": "ok",
|
|
31
|
-
"provider":
|
|
32
|
+
"provider": EMBEDDED_BACKEND_ID,
|
|
33
|
+
"optional_providers": ["ollama"],
|
|
34
|
+
"embedded": embedded_mini_brain_status(),
|
|
32
35
|
"mini_brain": contract,
|
|
33
36
|
"workers": [{"id": worker_id, "purpose": purpose} for worker_id, purpose in LOCAL_WORKERS],
|
|
34
37
|
"models": {
|
|
@@ -42,12 +45,14 @@ def local_llm_doctor() -> dict[str, Any]:
|
|
|
42
45
|
status = ollama_status()
|
|
43
46
|
contract = mini_brain_contract(ollama_payload=status)
|
|
44
47
|
model_plan = build_model_plan("resuma estes logs operacionais")
|
|
45
|
-
ok =
|
|
48
|
+
ok = contract.get("available") is True
|
|
46
49
|
return {
|
|
47
50
|
"kind": "local-llm-doctor",
|
|
48
51
|
"schema_version": LOCAL_LLM_SCHEMA_VERSION,
|
|
49
52
|
"status": "ok" if ok else "partial",
|
|
50
|
-
"provider":
|
|
53
|
+
"provider": EMBEDDED_BACKEND_ID,
|
|
54
|
+
"optional_providers": ["ollama"],
|
|
55
|
+
"embedded": embedded_mini_brain_status(),
|
|
51
56
|
"ollama": status,
|
|
52
57
|
"mini_brain": contract,
|
|
53
58
|
"model_plan": {
|
|
@@ -63,9 +68,19 @@ def local_llm_doctor() -> dict[str, Any]:
|
|
|
63
68
|
|
|
64
69
|
def local_llm_models() -> dict[str, Any]:
|
|
65
70
|
payload = ollama_models()
|
|
71
|
+
embedded = embedded_mini_brain_status()
|
|
66
72
|
payload["kind"] = "local-llm-models"
|
|
67
73
|
payload["schema_version"] = LOCAL_LLM_SCHEMA_VERSION
|
|
68
|
-
payload["provider"] =
|
|
74
|
+
payload["provider"] = EMBEDDED_BACKEND_ID
|
|
75
|
+
payload["embedded"] = {
|
|
76
|
+
"status": embedded.get("status"),
|
|
77
|
+
"provider": EMBEDDED_BACKEND_ID,
|
|
78
|
+
"model": EMBEDDED_MODEL_ID,
|
|
79
|
+
"installed": embedded.get("model_file_valid") is True,
|
|
80
|
+
"available": embedded.get("available") is True,
|
|
81
|
+
"install_command": embedded.get("install_command"),
|
|
82
|
+
}
|
|
83
|
+
payload["optional_provider"] = "ollama"
|
|
69
84
|
return payload
|
|
70
85
|
|
|
71
86
|
|
|
@@ -24,7 +24,7 @@ FORBIDDEN_DELEGATION_MARKERS = (
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
def maybe_delegate_local_llm(prompt: str, model_plan: dict[str, Any]) -> dict[str, Any]:
|
|
27
|
-
"""Execute a bounded operational task with
|
|
27
|
+
"""Execute a bounded operational task with the selected local worker."""
|
|
28
28
|
delegation = model_plan.get("delegation") if isinstance(model_plan.get("delegation"), dict) else {}
|
|
29
29
|
if model_plan.get("strategy") != "mini-brain":
|
|
30
30
|
return skipped(
|
|
@@ -38,6 +38,12 @@ def maybe_delegate_local_llm(prompt: str, model_plan: dict[str, Any]) -> dict[st
|
|
|
38
38
|
"High-risk tasks cannot be delegated to local LLM workers.",
|
|
39
39
|
model_plan=model_plan,
|
|
40
40
|
)
|
|
41
|
+
if model_plan.get("local_llm_role") != "operational-worker":
|
|
42
|
+
return skipped(
|
|
43
|
+
"not-operational-worker",
|
|
44
|
+
"The embedded mini-brain is acting as the bootstrap coordinator, not as a delegated worker.",
|
|
45
|
+
model_plan=model_plan,
|
|
46
|
+
)
|
|
41
47
|
if int(model_plan.get("max_llm_calls") or 0) <= 0:
|
|
42
48
|
return skipped(
|
|
43
49
|
"llm-budget-not-available",
|
|
@@ -50,9 +56,10 @@ def maybe_delegate_local_llm(prompt: str, model_plan: dict[str, Any]) -> dict[st
|
|
|
50
56
|
if any(marker in lowered for marker in FORBIDDEN_DELEGATION_MARKERS):
|
|
51
57
|
return skipped("forbidden", "Prompt contains an action that local LLM workers cannot execute.", model_plan=model_plan)
|
|
52
58
|
delegated_prompt = build_delegated_prompt(prompt, model_plan)
|
|
59
|
+
provider = str(model_plan.get("local_llm_provider") or "ollama")
|
|
53
60
|
result = invoke_agent_prompt(
|
|
54
61
|
delegated_prompt,
|
|
55
|
-
|
|
62
|
+
provider,
|
|
56
63
|
public_name="Local LLM Operator",
|
|
57
64
|
allow_fallback=False,
|
|
58
65
|
)
|
|
@@ -64,7 +71,7 @@ def maybe_delegate_local_llm(prompt: str, model_plan: dict[str, Any]) -> dict[st
|
|
|
64
71
|
"status": "ok" if result.get("ok") else result.get("status", "failed"),
|
|
65
72
|
"ok": bool(result.get("ok")),
|
|
66
73
|
"llm_backend": result.get("llm_backend"),
|
|
67
|
-
"model_provider":
|
|
74
|
+
"model_provider": provider,
|
|
68
75
|
"mini_brain": summarize_mini_brain(model_plan.get("mini_brain")),
|
|
69
76
|
"strategy": model_plan.get("strategy"),
|
|
70
77
|
"risk": model_plan.get("risk"),
|
|
@@ -108,7 +115,7 @@ def enrich_prompt_with_local_result(prompt: str, local_execution: dict[str, Any]
|
|
|
108
115
|
[
|
|
109
116
|
prompt,
|
|
110
117
|
"",
|
|
111
|
-
"Contexto operacional produzido pelo local-llm-operator/
|
|
118
|
+
f"Contexto operacional produzido pelo local-llm-operator/{local_execution.get('model_provider') or local_execution.get('llm_backend') or 'local'}:",
|
|
112
119
|
str(local_execution["response"]),
|
|
113
120
|
"",
|
|
114
121
|
"Use esse contexto apenas como apoio. A decisao, resposta final e revisao continuam sob responsabilidade do coordenador.",
|
|
@@ -131,7 +138,10 @@ def skipped(reason: str, message: str, *, model_plan: dict[str, Any]) -> dict[st
|
|
|
131
138
|
"strategy": model_plan.get("strategy"),
|
|
132
139
|
"risk": model_plan.get("risk"),
|
|
133
140
|
"confidence": model_plan.get("confidence"),
|
|
134
|
-
"requires_review": bool(
|
|
141
|
+
"requires_review": bool(
|
|
142
|
+
model_plan.get("local_llm_role") == "operational-worker"
|
|
143
|
+
and (model_plan.get("local_llm_recommended") or model_plan.get("local_llm_selected"))
|
|
144
|
+
),
|
|
135
145
|
}
|
|
136
146
|
|
|
137
147
|
|
|
@@ -5,14 +5,20 @@ from __future__ import annotations
|
|
|
5
5
|
from datetime import datetime, timezone
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
|
+
from cli.aikit.embedded_mini_brain import (
|
|
9
|
+
EMBEDDED_BACKEND_ID,
|
|
10
|
+
EMBEDDED_MODEL_ID,
|
|
11
|
+
embedded_mini_brain_status,
|
|
12
|
+
setup_embedded_mini_brain,
|
|
13
|
+
)
|
|
8
14
|
from cli.aikit.llm import BACKENDS, configure_backend, doctor_backend, load_config, save_config
|
|
9
|
-
from cli.aikit.ollama import
|
|
15
|
+
from cli.aikit.ollama import ollama_status
|
|
10
16
|
|
|
11
17
|
|
|
12
18
|
MINI_BRAIN_CONFIG_KEY = "mini_brain"
|
|
13
|
-
DEFAULT_HF_MODEL =
|
|
19
|
+
DEFAULT_HF_MODEL = EMBEDDED_MODEL_ID
|
|
14
20
|
DEFAULT_OLLAMA_MODEL = "qwen3:0.6b"
|
|
15
|
-
DEFAULT_PROVIDER =
|
|
21
|
+
DEFAULT_PROVIDER = EMBEDDED_BACKEND_ID
|
|
16
22
|
DEFAULT_BASE_URL = "http://localhost:11434/v1"
|
|
17
23
|
ALLOWED_TASKS = [
|
|
18
24
|
"setup_help",
|
|
@@ -50,14 +56,15 @@ def mini_brain_contract(
|
|
|
50
56
|
) -> dict[str, Any]:
|
|
51
57
|
config = load_config() if config is None else config
|
|
52
58
|
stored = config.get(MINI_BRAIN_CONFIG_KEY) if isinstance(config.get(MINI_BRAIN_CONFIG_KEY), dict) else {}
|
|
53
|
-
enabled = bool(stored.get("enabled"))
|
|
59
|
+
enabled = bool(stored.get("enabled", True))
|
|
54
60
|
provider = stored.get("provider") or stored.get("runtime") or DEFAULT_PROVIDER
|
|
55
61
|
hf_model = stored.get("hf_model") or stored.get("model") or DEFAULT_HF_MODEL
|
|
56
62
|
ollama_model = stored.get("ollama_model") or DEFAULT_OLLAMA_MODEL
|
|
63
|
+
embedded = embedded_mini_brain_status()
|
|
57
64
|
ollama_payload = ollama_status() if ollama_payload is None else ollama_payload
|
|
58
65
|
ollama_backend = doctor_backend(BACKENDS["ollama"], config) if ollama_backend is None else ollama_backend
|
|
59
|
-
|
|
60
|
-
runtime_available =
|
|
66
|
+
ollama_configured = ollama_backend.get("configured") is True
|
|
67
|
+
runtime_available = embedded.get("available") is True
|
|
61
68
|
available = enabled and provider == DEFAULT_PROVIDER and runtime_available
|
|
62
69
|
status = "ok" if available else "disabled" if not enabled else "unavailable"
|
|
63
70
|
return {
|
|
@@ -65,7 +72,8 @@ def mini_brain_contract(
|
|
|
65
72
|
"status": status,
|
|
66
73
|
"enabled": enabled,
|
|
67
74
|
"available": available,
|
|
68
|
-
"configured":
|
|
75
|
+
"configured": available,
|
|
76
|
+
"embedded_configured": provider == DEFAULT_PROVIDER,
|
|
69
77
|
"provider": provider,
|
|
70
78
|
"runtime": provider,
|
|
71
79
|
"hf_model": hf_model,
|
|
@@ -76,6 +84,7 @@ def mini_brain_contract(
|
|
|
76
84
|
"limits": dict_value(stored.get("limits"), DEFAULT_LIMITS),
|
|
77
85
|
"guardrails": list_value(stored.get("guardrails"), DEFAULT_GUARDRAILS),
|
|
78
86
|
"stored_secret": False,
|
|
87
|
+
"embedded": embedded,
|
|
79
88
|
"ollama": {
|
|
80
89
|
"status": ollama_payload.get("status"),
|
|
81
90
|
"daemon": (ollama_payload.get("daemon") or {}).get("status")
|
|
@@ -87,6 +96,7 @@ def mini_brain_contract(
|
|
|
87
96
|
"status": ollama_backend.get("status"),
|
|
88
97
|
"model": ollama_backend.get("model"),
|
|
89
98
|
"base_url": ollama_backend.get("base_url"),
|
|
99
|
+
"configured": ollama_configured,
|
|
90
100
|
},
|
|
91
101
|
}
|
|
92
102
|
|
|
@@ -98,6 +108,7 @@ def setup_mini_brain(
|
|
|
98
108
|
set_default: bool = False,
|
|
99
109
|
model: str = DEFAULT_OLLAMA_MODEL,
|
|
100
110
|
) -> dict[str, Any]:
|
|
111
|
+
embedded = embedded_mini_brain_status()
|
|
101
112
|
if dry_run or not yes:
|
|
102
113
|
status = "planned" if dry_run else "needs-confirmation"
|
|
103
114
|
needs_confirmation = not dry_run and not yes
|
|
@@ -110,53 +121,46 @@ def setup_mini_brain(
|
|
|
110
121
|
"yes": yes,
|
|
111
122
|
"stored_secret": False,
|
|
112
123
|
"mini_brain": planned_contract(model=model),
|
|
113
|
-
"
|
|
124
|
+
"embedded": embedded,
|
|
125
|
+
"embedded_install": setup_embedded_mini_brain(dry_run=True, yes=False),
|
|
126
|
+
"ollama_setup": {
|
|
127
|
+
"status": "skipped",
|
|
128
|
+
"ok": True,
|
|
129
|
+
"provider": "ollama",
|
|
130
|
+
"model": model,
|
|
131
|
+
"message": "Ollama is optional; use `agent local-llm install` to add local worker models.",
|
|
132
|
+
},
|
|
114
133
|
"next_steps": ["agent setup mini-brain --yes"],
|
|
115
|
-
"message": "Use --yes to
|
|
134
|
+
"message": "Use --yes to download and enable the embedded Qwen2.5-0.5B mini-brain.",
|
|
116
135
|
}
|
|
117
136
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
if
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
toolchain_install = install_toolchain(None, "ollama", dry_run=False, yes=True)
|
|
124
|
-
if toolchain_install.get("status") == "installed":
|
|
125
|
-
pull = ollama_pull(model, yes=True, dry_run=False)
|
|
126
|
-
if not pull.get("ok"):
|
|
127
|
-
payload = {
|
|
137
|
+
embedded_install = setup_embedded_mini_brain(dry_run=False, yes=True)
|
|
138
|
+
embedded = embedded_mini_brain_status()
|
|
139
|
+
if embedded_install.get("ok") is not True:
|
|
140
|
+
return {
|
|
128
141
|
"kind": "mini-brain-setup",
|
|
129
142
|
"status": "failed",
|
|
130
143
|
"ok": False,
|
|
131
|
-
"exit_code":
|
|
144
|
+
"exit_code": embedded_install.get("exit_code", 1),
|
|
132
145
|
"dry_run": False,
|
|
133
146
|
"yes": True,
|
|
134
147
|
"stored_secret": False,
|
|
135
|
-
"mini_brain":
|
|
136
|
-
"
|
|
137
|
-
"
|
|
138
|
-
"
|
|
148
|
+
"mini_brain": mini_brain_contract(),
|
|
149
|
+
"embedded": embedded,
|
|
150
|
+
"embedded_install": embedded_install,
|
|
151
|
+
"ollama_setup": {
|
|
152
|
+
"status": "skipped",
|
|
153
|
+
"ok": True,
|
|
154
|
+
"provider": "ollama",
|
|
155
|
+
"model": model,
|
|
156
|
+
"message": "Ollama remains optional for additional local worker models.",
|
|
157
|
+
},
|
|
158
|
+
"message": "Embedded mini-brain setup failed before the backend could be enabled.",
|
|
139
159
|
}
|
|
140
|
-
|
|
141
|
-
payload["toolchain_install"] = toolchain_install
|
|
142
|
-
payload["next_steps"] = [
|
|
143
|
-
"Review `agent toolchain doctor ollama`.",
|
|
144
|
-
"Run `agent toolchain install ollama --yes` if you approve external installation.",
|
|
145
|
-
"Then run `agent setup mini-brain --yes` again.",
|
|
146
|
-
]
|
|
147
|
-
return payload
|
|
148
|
-
|
|
149
|
-
existing_config = load_config()
|
|
150
|
-
existing_ollama = (
|
|
151
|
-
existing_config.get("llm", {}).get("backends", {}).get(DEFAULT_PROVIDER)
|
|
152
|
-
if isinstance(existing_config.get("llm"), dict)
|
|
153
|
-
else {}
|
|
154
|
-
)
|
|
155
|
-
existing_base_url = existing_ollama.get("base_url") if isinstance(existing_ollama, dict) else None
|
|
160
|
+
|
|
156
161
|
configured = configure_backend(
|
|
157
162
|
DEFAULT_PROVIDER,
|
|
158
|
-
|
|
159
|
-
model=model,
|
|
163
|
+
model=DEFAULT_HF_MODEL,
|
|
160
164
|
set_default=set_default,
|
|
161
165
|
)
|
|
162
166
|
config = load_config()
|
|
@@ -172,8 +176,15 @@ def setup_mini_brain(
|
|
|
172
176
|
"stored_secret": False,
|
|
173
177
|
"config_path": str(written_path),
|
|
174
178
|
"mini_brain": contract,
|
|
175
|
-
"
|
|
176
|
-
"
|
|
179
|
+
"embedded": embedded,
|
|
180
|
+
"embedded_install": embedded_install,
|
|
181
|
+
"ollama_setup": {
|
|
182
|
+
"status": "skipped",
|
|
183
|
+
"ok": True,
|
|
184
|
+
"provider": "ollama",
|
|
185
|
+
"model": model,
|
|
186
|
+
"message": "Ollama remains optional for additional local worker models.",
|
|
187
|
+
},
|
|
177
188
|
"llm_configure": configured,
|
|
178
189
|
"next_steps": ["Use low-risk setup, wizard and summary prompts normally."],
|
|
179
190
|
}
|
|
@@ -196,6 +207,7 @@ def planned_contract(*, model: str = DEFAULT_OLLAMA_MODEL) -> dict[str, Any]:
|
|
|
196
207
|
"limits": dict(DEFAULT_LIMITS),
|
|
197
208
|
"guardrails": list(DEFAULT_GUARDRAILS),
|
|
198
209
|
"stored_secret": False,
|
|
210
|
+
"embedded": embedded_mini_brain_status(),
|
|
199
211
|
}
|
|
200
212
|
|
|
201
213
|
|
|
@@ -5,6 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import re
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
|
+
from cli.aikit.embedded_mini_brain import EMBEDDED_BACKEND_ID
|
|
8
9
|
from cli.aikit.llm import BACKENDS, doctor_backend, llm_preference, load_config
|
|
9
10
|
from cli.aikit.mini_brain import mini_brain_contract
|
|
10
11
|
from cli.aikit.ollama import ollama_status
|
|
@@ -14,6 +15,9 @@ from cli.aikit.write_policy import normalize_write_policy, write_policy_public_f
|
|
|
14
15
|
OPERATIONAL_PATTERN = re.compile(
|
|
15
16
|
r"(?i)\b(resum\w*|sumari\w*|classifi\w*|extra(?:i|ir|ia|cao|ção)\w*|normaliz\w*|compar\w*|logs?|rascunho|agrupe|agrupar)\b"
|
|
16
17
|
)
|
|
18
|
+
SIMPLE_CHAT_SETUP_PATTERN = re.compile(
|
|
19
|
+
r"(?i)\b(ol[aá]|oi|bom dia|boa tarde|boa noite|ajuda|help|comec(?:ar|o)|começ(?:ar|o)|setup|onboard|configur|instal|usar)\b"
|
|
20
|
+
)
|
|
17
21
|
HIGH_LEVEL_PATTERN = re.compile(
|
|
18
22
|
r"(?i)\b(arquitet|decid|aprovar|reprovar|especifica|requisit|implemente|codigo|c[oó]digo|documento|automac|deploy|seguran)\b"
|
|
19
23
|
)
|
|
@@ -48,7 +52,9 @@ def build_model_plan(
|
|
|
48
52
|
mini_brain = mini_brain_contract(config=config, ollama_payload=ollama, ollama_backend=ollama_backend)
|
|
49
53
|
local_available = mini_brain.get("available") is True
|
|
50
54
|
operational = bool(OPERATIONAL_PATTERN.search(prompt))
|
|
55
|
+
simple_chat_setup = bool(SIMPLE_CHAT_SETUP_PATTERN.search(prompt))
|
|
51
56
|
high_level = bool(HIGH_LEVEL_PATTERN.search(prompt))
|
|
57
|
+
local_provider = select_local_provider(ollama_payload=ollama, ollama_backend=ollama_backend, mini_brain=mini_brain)
|
|
52
58
|
policy = choose_model_strategy(
|
|
53
59
|
prompt,
|
|
54
60
|
route=route,
|
|
@@ -56,10 +62,12 @@ def build_model_plan(
|
|
|
56
62
|
specialist_tasks=specialist_tasks or [],
|
|
57
63
|
configuration_tasks=configuration_tasks or [],
|
|
58
64
|
operational=operational,
|
|
65
|
+
simple_chat_setup=simple_chat_setup,
|
|
59
66
|
high_level=high_level,
|
|
60
67
|
local_available=local_available,
|
|
61
68
|
)
|
|
62
69
|
use_local = policy["strategy"] == "mini-brain" and local_available
|
|
70
|
+
delegate_local = use_local and operational
|
|
63
71
|
return {
|
|
64
72
|
"kind": "model-plan",
|
|
65
73
|
"status": "planned",
|
|
@@ -73,22 +81,30 @@ def build_model_plan(
|
|
|
73
81
|
"max_llm_calls": policy["max_llm_calls"],
|
|
74
82
|
"intent": route.get("intent") if route else "llm",
|
|
75
83
|
"primary_coordinators": coordinator_order(preference),
|
|
76
|
-
"local_llm_role": "operational-worker",
|
|
84
|
+
"local_llm_role": "operational-worker" if operational else "bootstrap-coordinator",
|
|
77
85
|
"local_llm_available": local_available,
|
|
78
|
-
"local_llm_provider":
|
|
79
|
-
"local_llm_backend_configured": ollama_backend.get("
|
|
86
|
+
"local_llm_provider": local_provider,
|
|
87
|
+
"local_llm_backend_configured": ollama_backend.get("configured") is True if local_provider == "ollama" else True,
|
|
80
88
|
"local_llm_runtime": {
|
|
81
|
-
"
|
|
89
|
+
"provider": local_provider,
|
|
90
|
+
"binary_status": ollama.get("status") if local_provider == "ollama" else "embedded",
|
|
82
91
|
"backend_status": ollama_backend.get("status"),
|
|
83
|
-
"model": mini_brain.get("ollama_model") or ollama_backend.get("model"),
|
|
84
|
-
"base_url": ollama_backend.get("base_url"),
|
|
92
|
+
"model": (mini_brain.get("ollama_model") or ollama_backend.get("model")) if local_provider == "ollama" else mini_brain.get("hf_model"),
|
|
93
|
+
"base_url": ollama_backend.get("base_url") if local_provider == "ollama" else None,
|
|
94
|
+
},
|
|
95
|
+
"optional_local_providers": {
|
|
96
|
+
"ollama": {
|
|
97
|
+
"status": ollama.get("status"),
|
|
98
|
+
"backend_status": ollama_backend.get("status"),
|
|
99
|
+
"model_count": ollama.get("model_count"),
|
|
100
|
+
}
|
|
85
101
|
},
|
|
86
102
|
"mini_brain": mini_brain,
|
|
87
103
|
"local_llm_recommended": operational,
|
|
88
104
|
"local_llm_selected": use_local,
|
|
89
105
|
"delegation": {
|
|
90
|
-
"allowed": policy["strategy"] == "mini-brain",
|
|
91
|
-
"selected":
|
|
106
|
+
"allowed": policy["strategy"] == "mini-brain" and operational,
|
|
107
|
+
"selected": delegate_local,
|
|
92
108
|
"reason": local_reason(
|
|
93
109
|
operational=operational,
|
|
94
110
|
local_available=local_available,
|
|
@@ -110,6 +126,7 @@ def choose_model_strategy(
|
|
|
110
126
|
specialist_tasks: list[dict[str, Any]],
|
|
111
127
|
configuration_tasks: list[dict[str, Any]],
|
|
112
128
|
operational: bool,
|
|
129
|
+
simple_chat_setup: bool,
|
|
113
130
|
high_level: bool,
|
|
114
131
|
local_available: bool,
|
|
115
132
|
) -> dict[str, Any]:
|
|
@@ -154,7 +171,7 @@ def choose_model_strategy(
|
|
|
154
171
|
max_llm_calls=0,
|
|
155
172
|
matrix="Conhecida + estruturada + baixo risco -> automacao",
|
|
156
173
|
)
|
|
157
|
-
if operational and not high_level:
|
|
174
|
+
if (operational or simple_chat_setup) and not high_level:
|
|
158
175
|
return policy(
|
|
159
176
|
"mini-brain" if local_available else "external-llm",
|
|
160
177
|
"The prompt is operational and low-risk; local mini-brain is preferred when available.",
|
|
@@ -241,3 +258,19 @@ def local_reason(*, operational: bool, local_available: bool, high_level: bool,
|
|
|
241
258
|
if operational and not local_available:
|
|
242
259
|
return "Task is operational, but the local mini-brain is not enabled or available; coordinator/API fallback should execute."
|
|
243
260
|
return "Task requires coordinator-level reasoning or review."
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def select_local_provider(
|
|
264
|
+
*,
|
|
265
|
+
ollama_payload: dict[str, Any],
|
|
266
|
+
ollama_backend: dict[str, Any],
|
|
267
|
+
mini_brain: dict[str, Any],
|
|
268
|
+
) -> str:
|
|
269
|
+
ollama_ready = (
|
|
270
|
+
ollama_payload.get("status") == "ok"
|
|
271
|
+
and ollama_backend.get("status") == "ok"
|
|
272
|
+
and (ollama_backend.get("configured") is True or int(ollama_payload.get("model_count") or 0) > 0)
|
|
273
|
+
)
|
|
274
|
+
if ollama_ready:
|
|
275
|
+
return "ollama"
|
|
276
|
+
return str(mini_brain.get("provider") or EMBEDDED_BACKEND_ID)
|
|
@@ -62,6 +62,21 @@ def clean_requested_name(value: str) -> str | None:
|
|
|
62
62
|
return cleaned[:80]
|
|
63
63
|
|
|
64
64
|
|
|
65
|
+
def rename_response(updated: dict[str, Any]) -> str:
|
|
66
|
+
name = updated.get("agent_name")
|
|
67
|
+
alias = updated.get("alias") if isinstance(updated.get("alias"), dict) else {}
|
|
68
|
+
alias_name = alias.get("name") or alias.get("suggested_name")
|
|
69
|
+
path_status = alias.get("path_status") if isinstance(alias.get("path_status"), dict) else {}
|
|
70
|
+
if alias_name and path_status.get("setup_required"):
|
|
71
|
+
return (
|
|
72
|
+
f"Pronto. Meu nome local agora e {name}, e criei o comando {alias_name}. "
|
|
73
|
+
"Para chama-lo diretamente em novas sessoes do shell, execute `agent alias path --yes`."
|
|
74
|
+
)
|
|
75
|
+
if alias_name:
|
|
76
|
+
return f"Pronto. Meu nome local agora e {name}, e voce tambem pode me chamar com o comando {alias_name}."
|
|
77
|
+
return f"Pronto. Meu nome local agora e {name}."
|
|
78
|
+
|
|
79
|
+
|
|
65
80
|
def is_capabilities_help_prompt(prompt: str) -> bool:
|
|
66
81
|
normalized = normalize_text(prompt)
|
|
67
82
|
if normalized in {"ajuda", "help", "o que voce faz", "o que voce consegue fazer"}:
|
|
@@ -138,6 +153,42 @@ def local_capabilities_help_response(prompt: str, *, name: str) -> dict[str, Any
|
|
|
138
153
|
}
|
|
139
154
|
|
|
140
155
|
|
|
156
|
+
def embedded_mini_brain_install_response(prompt: str, *, name: str, model_plan: dict[str, Any]) -> dict[str, Any]:
|
|
157
|
+
embedded = (
|
|
158
|
+
((model_plan.get("mini_brain") or {}).get("embedded") or {})
|
|
159
|
+
if isinstance(model_plan.get("mini_brain"), dict)
|
|
160
|
+
else {}
|
|
161
|
+
)
|
|
162
|
+
status = embedded.get("status") or "not-installed"
|
|
163
|
+
response = (
|
|
164
|
+
f"Eu sou {name}. Consigo orientar o setup inicial localmente, mas o mini-cerebro local ainda nao esta instalado "
|
|
165
|
+
f"(status: {status}). Para habilitar conversa local sem Claude, Codex, Ollama ou API externa, execute "
|
|
166
|
+
"`agent setup mini-brain --yes`. Sem esse download, posso continuar com onboarding, memoria, wizards e "
|
|
167
|
+
"capabilities deterministicas."
|
|
168
|
+
)
|
|
169
|
+
return {
|
|
170
|
+
"kind": "agent",
|
|
171
|
+
"status": "needs-setup",
|
|
172
|
+
"ok": False,
|
|
173
|
+
"requires_llm": False,
|
|
174
|
+
"prompt_received": True,
|
|
175
|
+
"prompt_length": len(prompt),
|
|
176
|
+
"mode": "embedded-mini-brain-not-installed",
|
|
177
|
+
"identity": {"name": name, "source": "local"},
|
|
178
|
+
"llm_backend": "embedded-mini-brain",
|
|
179
|
+
"mini_brain": model_plan.get("mini_brain"),
|
|
180
|
+
"response": response,
|
|
181
|
+
"message": "Embedded mini-brain is not installed yet.",
|
|
182
|
+
"next_steps": [
|
|
183
|
+
"agent setup mini-brain --dry-run",
|
|
184
|
+
"agent setup mini-brain --yes",
|
|
185
|
+
"agent llm configure claude-code --set-default",
|
|
186
|
+
"agent llm configure codex-cli --set-default",
|
|
187
|
+
],
|
|
188
|
+
"exit_code": 2,
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
|
|
141
192
|
def agent_requires_llm(args: argparse.Namespace) -> dict[str, Any]:
|
|
142
193
|
prompt = " ".join(args.prompt).strip()
|
|
143
194
|
return run_agent_prompt_request(
|
|
@@ -184,7 +235,8 @@ def run_agent_prompt_request(request: AgentPromptRequest) -> dict[str, Any]:
|
|
|
184
235
|
"prompt_length": len(prompt),
|
|
185
236
|
"identity": {"name": updated.get("agent_name"), "source": "local"},
|
|
186
237
|
"action": "rename",
|
|
187
|
-
"response":
|
|
238
|
+
"response": rename_response(updated),
|
|
239
|
+
"alias": updated.get("alias"),
|
|
188
240
|
}
|
|
189
241
|
return finalize_agent_session(result, session, prompt, backend=request.llm)
|
|
190
242
|
if is_identity_question(prompt):
|
|
@@ -237,9 +289,15 @@ def run_agent_prompt_request(request: AgentPromptRequest) -> dict[str, Any]:
|
|
|
237
289
|
)
|
|
238
290
|
local_llm_execution = maybe_delegate_local_llm(prompt, model_plan)
|
|
239
291
|
coordinator_prompt = enrich_prompt_with_local_result(contextual_prompt, local_llm_execution)
|
|
292
|
+
requested_backend = request.llm
|
|
293
|
+
if should_prompt_for_embedded_install(model_plan, requested_backend=request.llm):
|
|
294
|
+
result = embedded_mini_brain_install_response(prompt, name=name, model_plan=model_plan)
|
|
295
|
+
return finalize_agent_session(result, session, prompt, backend="embedded-mini-brain")
|
|
296
|
+
if should_use_embedded_coordinator(model_plan, requested_backend=request.llm):
|
|
297
|
+
requested_backend = "embedded-mini-brain"
|
|
240
298
|
result = invoke_agent_prompt(
|
|
241
299
|
coordinator_prompt,
|
|
242
|
-
|
|
300
|
+
requested_backend,
|
|
243
301
|
public_name=name,
|
|
244
302
|
allow_fallback=not request.no_llm_fallback,
|
|
245
303
|
)
|
|
@@ -278,6 +336,32 @@ def run_agent_prompt_request(request: AgentPromptRequest) -> dict[str, Any]:
|
|
|
278
336
|
return finalize_agent_session(result, session, prompt, backend=result.get("llm_backend") or request.llm)
|
|
279
337
|
|
|
280
338
|
|
|
339
|
+
def should_use_embedded_coordinator(model_plan: dict[str, Any], *, requested_backend: str | None) -> bool:
|
|
340
|
+
if requested_backend:
|
|
341
|
+
return False
|
|
342
|
+
return (
|
|
343
|
+
model_plan.get("strategy") == "mini-brain"
|
|
344
|
+
and model_plan.get("local_llm_provider") == "embedded-mini-brain"
|
|
345
|
+
and model_plan.get("risk") == "low"
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def should_prompt_for_embedded_install(model_plan: dict[str, Any], *, requested_backend: str | None) -> bool:
|
|
350
|
+
if requested_backend:
|
|
351
|
+
return False
|
|
352
|
+
embedded = (
|
|
353
|
+
((model_plan.get("mini_brain") or {}).get("embedded") or {})
|
|
354
|
+
if isinstance(model_plan.get("mini_brain"), dict)
|
|
355
|
+
else {}
|
|
356
|
+
)
|
|
357
|
+
return (
|
|
358
|
+
model_plan.get("strategy") in {"mini-brain", "external-llm"}
|
|
359
|
+
and model_plan.get("local_llm_provider") == "embedded-mini-brain"
|
|
360
|
+
and embedded.get("available") is not True
|
|
361
|
+
and model_plan.get("fallback") == "configure-local-mini-brain-or-use-external-llm"
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
|
|
281
365
|
def mark_review_task_needs_review(execution_plan: dict[str, Any], review_result: dict[str, Any]) -> dict[str, Any]:
|
|
282
366
|
task = dict(execution_plan.get("review_task") or {})
|
|
283
367
|
if task:
|
|
@@ -106,16 +106,16 @@ def onboarding_plan(root: Path, mode: str) -> dict[str, Any]:
|
|
|
106
106
|
),
|
|
107
107
|
plan_step(
|
|
108
108
|
"coordinator-llm",
|
|
109
|
-
"Registrar Claude Code, Codex CLI ou API como coordenador/planejador/revisor.",
|
|
109
|
+
"Registrar Claude Code, Codex CLI ou API como coordenador/planejador/revisor opcional para tarefas de alto nivel.",
|
|
110
110
|
"agent llm list",
|
|
111
111
|
write_policy="local_config_write",
|
|
112
112
|
),
|
|
113
113
|
plan_step(
|
|
114
114
|
"mini-brain",
|
|
115
|
-
"
|
|
115
|
+
"Validar o mini cerebro embarcado Qwen2.5-0.5B para conversa simples, setup e tarefas operacionais leves.",
|
|
116
116
|
"agent setup mini-brain --dry-run",
|
|
117
117
|
write_policy="local_config_write",
|
|
118
|
-
model="
|
|
118
|
+
model="Qwen/Qwen2.5-0.5B-Instruct",
|
|
119
119
|
),
|
|
120
120
|
plan_step(
|
|
121
121
|
"sessions-and-memory",
|
|
@@ -6,6 +6,7 @@ import re
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from typing import Any
|
|
8
8
|
|
|
9
|
+
from cli.aikit.aliases import ensure_alias_for_agent_name
|
|
9
10
|
from cli.aikit.identity import DEFAULT_PUBLIC_NAME
|
|
10
11
|
from cli.aikit.memory import MEMORY_FILE_TEMPLATES, ensure_memory, memory_home
|
|
11
12
|
|
|
@@ -60,6 +61,8 @@ def update_personality(
|
|
|
60
61
|
path.write_text(render_personality(values), encoding="utf-8")
|
|
61
62
|
payload = load_personality()
|
|
62
63
|
payload["status"] = "updated"
|
|
64
|
+
if agent_name is not None and payload.get("agent_name") != DEFAULT_PUBLIC_NAME:
|
|
65
|
+
payload["alias"] = ensure_alias_for_agent_name(str(payload["agent_name"]))
|
|
63
66
|
return payload
|
|
64
67
|
|
|
65
68
|
|