arkaos 4.8.0 → 4.10.0
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/VERSION +1 -1
- package/arka/skills/research/SKILL.md +6 -6
- package/bin/arka +5 -1
- package/bin/arkaos +2 -2
- package/config/disc-team-validator.sh +7 -7
- package/config/evals/dev.yaml +28 -0
- package/config/evals/finance.yaml +28 -0
- package/config/evals/kb.yaml +26 -0
- package/config/evals/marketing.yaml +28 -0
- package/config/evals/strategy.yaml +26 -0
- package/core/agents/__pycache__/registry_gen.cpython-313.pyc +0 -0
- package/core/agents/registry_gen.py +6 -4
- package/core/evals/__init__.py +19 -0
- package/core/evals/__pycache__/__init__.cpython-313.pyc +0 -0
- package/core/evals/__pycache__/schema.cpython-313.pyc +0 -0
- package/core/evals/__pycache__/verdict_labels.cpython-313.pyc +0 -0
- package/core/evals/schema.py +54 -0
- package/core/evals/verdict_labels.py +72 -0
- package/core/registry/__pycache__/__init__.cpython-312.pyc +0 -0
- package/core/registry/__pycache__/generator.cpython-312.pyc +0 -0
- package/core/registry/__pycache__/generator.cpython-313.pyc +0 -0
- package/core/registry/generator.py +138 -110
- package/core/runtime/__pycache__/llm_cost_telemetry.cpython-313.pyc +0 -0
- package/core/runtime/__pycache__/llm_cost_telemetry.cpython-314.pyc +0 -0
- package/core/runtime/llm_cost_telemetry.py +13 -1
- package/core/synapse/__pycache__/engine.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/engine.cpython-314.pyc +0 -0
- package/core/synapse/__pycache__/layers.cpython-313.pyc +0 -0
- package/core/synapse/__pycache__/layers.cpython-314.pyc +0 -0
- package/core/synapse/engine.py +6 -0
- package/core/synapse/layers.py +17 -0
- package/departments/dev/skills/research/SKILL.md +3 -2
- package/departments/dev/skills/scaffold/SKILL.md +3 -3
- package/knowledge/commands-registry.json +4194 -2627
- package/knowledge/commands-registry.json.bak +4194 -2627
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/scripts/__pycache__/dashboard-api.cpython-313.pyc +0 -0
- package/scripts/__pycache__/synapse-bridge.cpython-313.pyc +0 -0
- package/scripts/__pycache__/synapse-bridge.cpython-314.pyc +0 -0
- package/scripts/dashboard-api.py +2 -2
- package/scripts/synapse-bridge.py +1 -1
- package/scripts/tools/__pycache__/prompt_surface_benchmark.cpython-313.pyc +0 -0
- package/scripts/tools/prompt_surface_benchmark.py +144 -0
- package/knowledge/agents-registry.json +0 -254
- package/knowledge/commands-registry-v2.json +0 -3827
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
4.
|
|
1
|
+
4.10.0
|
|
@@ -6,13 +6,13 @@ description: >
|
|
|
6
6
|
single cited report, and writes a Knowledge Base note to the Obsidian
|
|
7
7
|
vault. KB-first: Obsidian is searched before any external call.
|
|
8
8
|
TRIGGER: "/arka research", "pesquisa sobre", "investiga",
|
|
9
|
-
"faz research", "research <topic>", "best practices for"
|
|
10
|
-
the art", "o que se sabe sobre" — general, market,
|
|
11
|
-
topics whose deliverable is a synthesised KB note.
|
|
9
|
+
"faz research", "research <topic>", "best practices for" non-code
|
|
10
|
+
topics, "state of the art", "o que se sabe sobre" — general, market,
|
|
11
|
+
tooling, or KB topics whose deliverable is a synthesised KB note.
|
|
12
12
|
SKIP: dev-scoped technical research — library or framework evaluation,
|
|
13
|
-
"que lib uso", "which library/framework"
|
|
14
|
-
/
|
|
15
|
-
|
|
13
|
+
"que lib uso", "which library/framework", best-practice questions
|
|
14
|
+
about implementation choices — dev/research (Lucas) wins; planning a
|
|
15
|
+
task — arka-forge wins.
|
|
16
16
|
allowed-tools: [Agent, Read, Write, mcp__obsidian__search_notes]
|
|
17
17
|
---
|
|
18
18
|
|
package/bin/arka
CHANGED
|
@@ -137,7 +137,11 @@ case "${1:-}" in
|
|
|
137
137
|
REGISTRY="$REPO_DIR/knowledge/commands-registry.json"
|
|
138
138
|
case "${1:-}" in
|
|
139
139
|
rebuild)
|
|
140
|
-
|
|
140
|
+
# Shared resolver (venv-first) with plain python3 fallback —
|
|
141
|
+
# the generator is stdlib-only.
|
|
142
|
+
_ARKA_LIB="$REPO_DIR/config/hooks/_lib/arka_python.sh"
|
|
143
|
+
if [ -f "$_ARKA_LIB" ]; then . "$_ARKA_LIB"; else ARKA_PY="python3"; fi
|
|
144
|
+
(cd "$REPO_DIR" && "$ARKA_PY" -m core.registry.generator)
|
|
141
145
|
echo "Registry rebuilt: $(jq '._meta.total_commands' "$REGISTRY") commands."
|
|
142
146
|
;;
|
|
143
147
|
--json)
|
package/bin/arkaos
CHANGED
|
@@ -65,8 +65,8 @@ case "${1:-}" in
|
|
|
65
65
|
agents=$(jq -r '._meta.total_agents // 0' "$REPO_ROOT/knowledge/agents-registry-v2.json")
|
|
66
66
|
echo " Agents: ${agents}"
|
|
67
67
|
fi
|
|
68
|
-
if [ -n "$REPO_ROOT" ] && [ -f "$REPO_ROOT/knowledge/commands-registry
|
|
69
|
-
commands=$(jq -r '._meta.total_commands // 0' "$REPO_ROOT/knowledge/commands-registry
|
|
68
|
+
if [ -n "$REPO_ROOT" ] && [ -f "$REPO_ROOT/knowledge/commands-registry.json" ] && command -v jq &>/dev/null; then
|
|
69
|
+
commands=$(jq -r '._meta.total_commands // 0' "$REPO_ROOT/knowledge/commands-registry.json")
|
|
70
70
|
echo " Commands: ${commands}"
|
|
71
71
|
fi
|
|
72
72
|
echo ""
|
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# ============================================================================
|
|
3
3
|
# ARKA OS — DISC Team Balance Validator
|
|
4
|
-
# Reads agents-registry.json and validates team DISC distribution
|
|
4
|
+
# Reads agents-registry-v2.json and validates team DISC distribution
|
|
5
5
|
# ============================================================================
|
|
6
6
|
set -e
|
|
7
7
|
|
|
8
8
|
ARKA_OS="${ARKA_OS:-$HOME/.claude/skills/arka}"
|
|
9
9
|
REPO_DIR="${REPO_DIR:-$(cat "$ARKA_OS/.repo-path" 2>/dev/null || echo "")}"
|
|
10
|
-
REGISTRY="${REGISTRY:-$REPO_DIR/knowledge/agents-registry.json}"
|
|
10
|
+
REGISTRY="${REGISTRY:-$REPO_DIR/knowledge/agents-registry-v2.json}"
|
|
11
11
|
|
|
12
12
|
if [ ! -f "$REGISTRY" ]; then
|
|
13
|
-
echo "Error: agents-registry.json not found at $REGISTRY"
|
|
13
|
+
echo "Error: agents-registry-v2.json not found at $REGISTRY"
|
|
14
14
|
exit 1
|
|
15
15
|
fi
|
|
16
16
|
|
|
17
17
|
command -v jq &>/dev/null || { echo "Error: jq is required."; exit 1; }
|
|
18
18
|
|
|
19
19
|
# Read counts from registry
|
|
20
|
-
D_COUNT=$(jq '.
|
|
21
|
-
I_COUNT=$(jq '.
|
|
22
|
-
S_COUNT=$(jq '.
|
|
23
|
-
C_COUNT=$(jq '.
|
|
20
|
+
D_COUNT=$(jq '._meta.disc_distribution.D' "$REGISTRY")
|
|
21
|
+
I_COUNT=$(jq '._meta.disc_distribution.I' "$REGISTRY")
|
|
22
|
+
S_COUNT=$(jq '._meta.disc_distribution.S' "$REGISTRY")
|
|
23
|
+
C_COUNT=$(jq '._meta.disc_distribution.C' "$REGISTRY")
|
|
24
24
|
TOTAL=$(( D_COUNT + I_COUNT + S_COUNT + C_COUNT ))
|
|
25
25
|
|
|
26
26
|
# Calculate percentages
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Dev department reference eval tasks (seed set).
|
|
2
|
+
# Judged against expected_properties + QG verdict; see core/evals/schema.py.
|
|
3
|
+
- id: dev-feature-auth-endpoint
|
|
4
|
+
department: dev
|
|
5
|
+
prompt: >
|
|
6
|
+
Implementa um endpoint de autenticação por token (login + refresh)
|
|
7
|
+
num serviço Laravel existente, com testes.
|
|
8
|
+
expected_properties:
|
|
9
|
+
- FormRequest valida credenciais (nunca validação inline no controller)
|
|
10
|
+
- Lógica em Service/Repository, controller fino
|
|
11
|
+
- Feature tests cobrem sucesso, credenciais inválidas e refresh expirado
|
|
12
|
+
- Nenhum segredo hardcoded; usa config/env
|
|
13
|
+
rubric: >
|
|
14
|
+
Rejeitar se os testes não correram (exit code em registo) ou se o
|
|
15
|
+
fluxo de refresh não invalida o token antigo.
|
|
16
|
+
tags: [backend, laravel, security-adjacent]
|
|
17
|
+
|
|
18
|
+
- id: dev-refactor-n-plus-one
|
|
19
|
+
department: dev
|
|
20
|
+
prompt: >
|
|
21
|
+
Este listing de encomendas faz N+1 queries; corrige sem alterar o
|
|
22
|
+
contrato JSON da API.
|
|
23
|
+
expected_properties:
|
|
24
|
+
- Eager loading ou query única substitui o loop de queries
|
|
25
|
+
- Contrato de resposta byte-idêntico (teste de regressão prova-o)
|
|
26
|
+
- Query count coberto por teste (assertQueryCount ou equivalente)
|
|
27
|
+
rubric: Rejeitar qualquer alteração de schema de resposta.
|
|
28
|
+
tags: [backend, performance]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Finance department reference eval tasks (seed set).
|
|
2
|
+
- id: fin-unit-economics-saas
|
|
3
|
+
department: finance
|
|
4
|
+
prompt: >
|
|
5
|
+
Calcula os unit economics de um SaaS com MRR 8000 EUR, 120 clientes,
|
|
6
|
+
churn mensal 3%, CAC 250 EUR e margem bruta 80%.
|
|
7
|
+
expected_properties:
|
|
8
|
+
- LTV derivado com fórmula explícita (ARPU, churn, margem)
|
|
9
|
+
- Rácio LTV/CAC calculado e interpretado contra o benchmark 3x
|
|
10
|
+
- Payback period em meses
|
|
11
|
+
- Aritmética verificável (números batem certo)
|
|
12
|
+
rubric: >
|
|
13
|
+
Rejeitar qualquer erro aritmético — o arkaos-not-yes-man aplica-se a
|
|
14
|
+
matemática impossível do próprio input.
|
|
15
|
+
tags: [unit-economics, saas]
|
|
16
|
+
|
|
17
|
+
- id: fin-cashflow-13-weeks
|
|
18
|
+
department: finance
|
|
19
|
+
prompt: >
|
|
20
|
+
Constrói um forecast de cash flow a 13 semanas para uma agência com
|
|
21
|
+
3 clientes em retainer e folha salarial fixa.
|
|
22
|
+
expected_properties:
|
|
23
|
+
- Entradas por cliente com datas de recebimento realistas
|
|
24
|
+
- Saídas fixas e variáveis separadas
|
|
25
|
+
- Saldo semanal acumulado com semana crítica identificada
|
|
26
|
+
- Cenário pessimista (cliente atrasa 30 dias) incluído
|
|
27
|
+
rubric: Rejeitar forecast sem cenário de stress.
|
|
28
|
+
tags: [cashflow, forecast]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Knowledge department reference eval tasks (seed set).
|
|
2
|
+
- id: kb-research-note-synthesis
|
|
3
|
+
department: kb
|
|
4
|
+
prompt: >
|
|
5
|
+
Pesquisa "estratégias de retry para APIs rate-limited" e produz uma
|
|
6
|
+
nota KB sintetizada com fontes.
|
|
7
|
+
expected_properties:
|
|
8
|
+
- KB consultada primeiro (citação [[wikilink]] ou gap declarado)
|
|
9
|
+
- Fontes externas citadas com claims atribuídos
|
|
10
|
+
- Nota com frontmatter YAML + wikilinks + estrutura Zettelkasten
|
|
11
|
+
- Síntese própria, não colagem de excertos
|
|
12
|
+
rubric: Rejeitar claims sem fonte ou nota sem ligações ao grafo.
|
|
13
|
+
tags: [research, zettelkasten]
|
|
14
|
+
|
|
15
|
+
- id: kb-moc-reorganize
|
|
16
|
+
department: kb
|
|
17
|
+
prompt: >
|
|
18
|
+
Organiza 15 notas soltas sobre "pricing" num MOC (Map of Content)
|
|
19
|
+
coerente no vault.
|
|
20
|
+
expected_properties:
|
|
21
|
+
- MOC com agrupamento temático justificado
|
|
22
|
+
- Todas as 15 notas ligadas (nenhuma órfã)
|
|
23
|
+
- Hierarquia máxima de 2 níveis
|
|
24
|
+
- Notas duplicadas identificadas para merge
|
|
25
|
+
rubric: Rejeitar agrupamento alfabético ou sem lógica temática.
|
|
26
|
+
tags: [moc, organization]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Marketing department reference eval tasks (seed set).
|
|
2
|
+
- id: mkt-launch-campaign-plan
|
|
3
|
+
department: marketing
|
|
4
|
+
prompt: >
|
|
5
|
+
Desenha o plano de campanha de lançamento (4 semanas) para um
|
|
6
|
+
micro-SaaS B2B de gestão de inventário, orçamento 2000 EUR.
|
|
7
|
+
expected_properties:
|
|
8
|
+
- Estrutura AARRR explícita com métrica alvo por etapa
|
|
9
|
+
- Canais escolhidos justificados contra o ICP (não lista genérica)
|
|
10
|
+
- Orçamento alocado por canal soma 2000 EUR
|
|
11
|
+
- Calendário semanal com owners
|
|
12
|
+
rubric: >
|
|
13
|
+
Rejeitar clichés de AI copy e canais sem justificação de fit; o
|
|
14
|
+
framework citado tem de ser aplicado, não só nomeado.
|
|
15
|
+
tags: [gtm, aarrr, budget]
|
|
16
|
+
|
|
17
|
+
- id: mkt-seo-content-brief
|
|
18
|
+
department: marketing
|
|
19
|
+
prompt: >
|
|
20
|
+
Cria um brief SEO para um artigo pilar sobre "supplier sync para
|
|
21
|
+
Shopify" com intenção de pesquisa comercial.
|
|
22
|
+
expected_properties:
|
|
23
|
+
- Keyword primária + secundárias com intenção classificada
|
|
24
|
+
- Outline H2/H3 cobre as perguntas do SERP
|
|
25
|
+
- Internal links e CTA definidos
|
|
26
|
+
- Meta title/description dentro dos limites de caracteres
|
|
27
|
+
rubric: Rejeitar keyword stuffing ou outline sem lógica de funil.
|
|
28
|
+
tags: [seo, content]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Strategy department reference eval tasks (seed set).
|
|
2
|
+
- id: strat-five-forces-entry
|
|
3
|
+
department: strategy
|
|
4
|
+
prompt: >
|
|
5
|
+
Avalia com Porter's Five Forces a entrada de um novo player no
|
|
6
|
+
mercado de ferramentas de orquestração de agentes AI.
|
|
7
|
+
expected_properties:
|
|
8
|
+
- As 5 forças analisadas com evidência específica do mercado, não genérica
|
|
9
|
+
- Intensidade classificada por força com justificação
|
|
10
|
+
- Conclusão acionável (entrar/não entrar/entrar com que moat)
|
|
11
|
+
rubric: >
|
|
12
|
+
Rejeitar análise que apenas define as forças sem as aplicar ao
|
|
13
|
+
mercado concreto.
|
|
14
|
+
tags: [porter, market-entry]
|
|
15
|
+
|
|
16
|
+
- id: strat-moat-audit
|
|
17
|
+
department: strategy
|
|
18
|
+
prompt: >
|
|
19
|
+
Audita os moats de um produto dev-tools open-source com dashboard
|
|
20
|
+
pago, usando 7 Powers.
|
|
21
|
+
expected_properties:
|
|
22
|
+
- Cada power avaliada como presente/ausente/construível
|
|
23
|
+
- Pelo menos um trade-off identificado (ex. open-source vs counter-positioning)
|
|
24
|
+
- Recomendação priorizada de qual power investir primeiro
|
|
25
|
+
rubric: Rejeitar se citar 7 Powers sem mapear ao produto.
|
|
26
|
+
tags: [7-powers, moat]
|
|
Binary file
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
"""Generate agents-registry.json from all agent YAML files.
|
|
1
|
+
"""Generate agents-registry-v2.json from all agent YAML files.
|
|
2
2
|
|
|
3
|
-
Scans departments/*/agents/*.yaml and produces
|
|
4
|
-
registry with all agent metadata including behavioral
|
|
3
|
+
Scans departments/*/agents/*.yaml and produces the single canonical
|
|
4
|
+
machine-readable registry with all agent metadata including behavioral
|
|
5
|
+
DNA. The legacy knowledge/agents-registry.json (v1) was removed —
|
|
6
|
+
tests/python/test_registry_gen.py guards against its resurrection.
|
|
5
7
|
"""
|
|
6
8
|
|
|
7
9
|
import json
|
|
@@ -12,7 +14,7 @@ from core.agents.loader import load_agent
|
|
|
12
14
|
|
|
13
15
|
|
|
14
16
|
def generate_registry(departments_dir: str | Path, output_path: str | Path) -> dict:
|
|
15
|
-
"""Generate agents-registry.json from YAML agent files.
|
|
17
|
+
"""Generate agents-registry-v2.json from YAML agent files.
|
|
16
18
|
|
|
17
19
|
Args:
|
|
18
20
|
departments_dir: Path to departments/ directory.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Eval harness foundation (E2E audit v4.3.6 P2).
|
|
2
|
+
|
|
3
|
+
Reference eval tasks per department (config/evals/*.yaml) plus the
|
|
4
|
+
QG-verdict label log — the free labeled dataset every future eval run
|
|
5
|
+
or local-model distillation consumes.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from core.evals.schema import EvalTask, load_eval_tasks
|
|
9
|
+
from core.evals.verdict_labels import (
|
|
10
|
+
load_verdict_labels,
|
|
11
|
+
record_verdict_label,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"EvalTask",
|
|
16
|
+
"load_eval_tasks",
|
|
17
|
+
"load_verdict_labels",
|
|
18
|
+
"record_verdict_label",
|
|
19
|
+
]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Eval task schema and loader.
|
|
2
|
+
|
|
3
|
+
Reference tasks live in ``config/evals/<department>.yaml`` — one YAML
|
|
4
|
+
list per department, versioned with the repo so evals evolve with the
|
|
5
|
+
agents they measure. See ADR 2026-07-09-evals-and-distillation.md.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
import yaml
|
|
13
|
+
from pydantic import BaseModel, Field
|
|
14
|
+
|
|
15
|
+
DEFAULT_EVALS_DIR = (
|
|
16
|
+
Path(__file__).resolve().parent.parent.parent / "config" / "evals"
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class EvalTask(BaseModel):
|
|
21
|
+
"""One reference task an agent/department run is judged against."""
|
|
22
|
+
|
|
23
|
+
id: str = Field(pattern=r"^[a-z0-9][a-z0-9-]*$")
|
|
24
|
+
department: str
|
|
25
|
+
prompt: str = Field(min_length=10)
|
|
26
|
+
expected_properties: list[str] = Field(
|
|
27
|
+
min_length=1,
|
|
28
|
+
description="Verifiable properties the deliverable must exhibit",
|
|
29
|
+
)
|
|
30
|
+
rubric: str = Field(
|
|
31
|
+
default="",
|
|
32
|
+
description="Free-text judging guidance beyond the properties",
|
|
33
|
+
)
|
|
34
|
+
tags: list[str] = Field(default_factory=list)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def load_eval_tasks(evals_dir: Path | None = None) -> list[EvalTask]:
|
|
38
|
+
"""Load every task from config/evals/*.yaml, validated.
|
|
39
|
+
|
|
40
|
+
Raises on schema violations or duplicate ids — a broken eval set
|
|
41
|
+
must fail loudly, not skew results silently.
|
|
42
|
+
"""
|
|
43
|
+
base = evals_dir or DEFAULT_EVALS_DIR
|
|
44
|
+
tasks: list[EvalTask] = []
|
|
45
|
+
seen: set[str] = set()
|
|
46
|
+
for path in sorted(base.glob("*.yaml")):
|
|
47
|
+
raw = yaml.safe_load(path.read_text(encoding="utf-8")) or []
|
|
48
|
+
for item in raw:
|
|
49
|
+
task = EvalTask.model_validate(item)
|
|
50
|
+
if task.id in seen:
|
|
51
|
+
raise ValueError(f"duplicate eval task id: {task.id}")
|
|
52
|
+
seen.add(task.id)
|
|
53
|
+
tasks.append(task)
|
|
54
|
+
return tasks
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""QG verdicts as free eval labels.
|
|
2
|
+
|
|
3
|
+
Every Quality Gate review already produces a structured ``QGVerdict``
|
|
4
|
+
(APPROVED/REJECTED + blockers + evidence summary). Persisting them turns
|
|
5
|
+
each review into a labeled example — the training/eval signal the E2E
|
|
6
|
+
audit called "labels gratuitos". Append-only JSONL, same conventions as
|
|
7
|
+
the other ``~/.arkaos/telemetry`` writers: never raises, fcntl advisory
|
|
8
|
+
lock via ``llm_cost_telemetry._locked_append`` (QG reviews run as
|
|
9
|
+
parallel subagents and verdict lines far exceed atomic-write sizes, so
|
|
10
|
+
unlocked appends could interleave and corrupt the label corpus),
|
|
11
|
+
``ARKA_QG_LABELS_PATH`` override for tests.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import json
|
|
17
|
+
import os
|
|
18
|
+
from datetime import datetime, timezone
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Any
|
|
21
|
+
|
|
22
|
+
from core.governance.qg_verdict import QGVerdict
|
|
23
|
+
from core.runtime.llm_cost_telemetry import _locked_append
|
|
24
|
+
|
|
25
|
+
DEFAULT_LABELS_PATH = (
|
|
26
|
+
Path.home() / ".arkaos" / "telemetry" / "qg-verdicts.jsonl"
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _labels_path() -> Path:
|
|
31
|
+
override = os.environ.get("ARKA_QG_LABELS_PATH", "").strip()
|
|
32
|
+
return Path(override) if override else DEFAULT_LABELS_PATH
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def record_verdict_label(
|
|
36
|
+
verdict: QGVerdict,
|
|
37
|
+
deliverable: str = "",
|
|
38
|
+
department: str = "",
|
|
39
|
+
eval_task_id: str = "",
|
|
40
|
+
session_id: str = "",
|
|
41
|
+
) -> None:
|
|
42
|
+
"""Append one labeled QG example. Never raises (telemetry contract)."""
|
|
43
|
+
try:
|
|
44
|
+
entry: dict[str, Any] = {
|
|
45
|
+
"ts": datetime.now(timezone.utc).isoformat(),
|
|
46
|
+
"deliverable": str(deliverable or ""),
|
|
47
|
+
"department": str(department or ""),
|
|
48
|
+
"eval_task_id": str(eval_task_id or ""),
|
|
49
|
+
"session_id": str(session_id or ""),
|
|
50
|
+
**verdict.model_dump(),
|
|
51
|
+
}
|
|
52
|
+
with _locked_append(_labels_path()) as fh:
|
|
53
|
+
fh.write(json.dumps(entry, ensure_ascii=False) + "\n")
|
|
54
|
+
except Exception: # noqa: BLE001 — telemetry must never raise
|
|
55
|
+
return
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def load_verdict_labels(path: Path | None = None) -> list[dict[str, Any]]:
|
|
59
|
+
"""Read all labeled examples; malformed lines are skipped."""
|
|
60
|
+
target = path or _labels_path()
|
|
61
|
+
if not target.exists():
|
|
62
|
+
return []
|
|
63
|
+
out: list[dict[str, Any]] = []
|
|
64
|
+
for line in target.read_text(encoding="utf-8").splitlines():
|
|
65
|
+
line = line.strip()
|
|
66
|
+
if not line:
|
|
67
|
+
continue
|
|
68
|
+
try:
|
|
69
|
+
out.append(json.loads(line))
|
|
70
|
+
except json.JSONDecodeError:
|
|
71
|
+
continue
|
|
72
|
+
return out
|
|
Binary file
|
|
Binary file
|
|
Binary file
|