426e9eeabd
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
ci / test (push) Has been cancelled
ci / lint-and-format (push) Has been cancelled
ci / build (push) Has been cancelled
ci / dev-startup (push) Has been cancelled
gitleaks / gitleaks (push) Has been cancelled
Markdown Links / Relative Markdown Links (push) Has been cancelled
Quality (Extended) / Homepage Build (PR smoke) (push) Has been cancelled
Quality (Extended) / Comment-only diff guard (push) Has been cancelled
Quality (Extended) / Format + Type Safety Ratchet (push) Has been cancelled
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Has been cancelled
Quality (Extended) / Develop Gate (lint) (push) Has been cancelled
Chat shell gestures / Chat shell gesture + parity e2e (push) Has been cancelled
Cloud Gateway Discord / Test (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Has been cancelled
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Has been cancelled
Build Agent Image / build-and-push (push) Has been cancelled
Dev Smoke / bun run dev onboarding chat (push) Has been cancelled
Dev Smoke / Vite HMR dependency-level smoke (push) Has been cancelled
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Has been cancelled
Publish @elizaos/example-code / check_npm (push) Has been cancelled
Publish @elizaos/example-code / publish_npm (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / verify_version (push) Has been cancelled
Publish @elizaos/plugin-elizacloud / publish_npm (push) Has been cancelled
Sandbox Live Smoke / Sandbox live smoke (push) Has been cancelled
Snap Build & Test / Build Snap (amd64) (push) Has been cancelled
Snap Build & Test / Build Snap (arm64) (push) Has been cancelled
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Has been cancelled
Cloud Gateway Webhook / Test (push) Has been cancelled
Cloud Tests / lint-and-types (push) Has been cancelled
Cloud Tests / unit-tests (push) Has been cancelled
Cloud Tests / integration-tests (push) Has been cancelled
Cloud Tests / e2e-tests (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Deploy Apps Worker (Product 2) / Determine environment (push) Has been cancelled
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Has been cancelled
Deploy Eliza Provisioning Worker / Determine environment (push) Has been cancelled
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Has been cancelled
Dev Smoke / Classify changed paths (push) Has been cancelled
supply-chain / sbom (push) Has been cancelled
supply-chain / vulnerability-scan (push) Has been cancelled
Build, Push & Deploy to Phala Cloud / build-and-push (push) Has been cancelled
Test Packaging / Validate Packaging Configs (push) Has been cancelled
Test Packaging / Build & Test PyPI Package (push) Has been cancelled
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Has been cancelled
Test Packaging / Pack & Test JS Tarballs (push) Has been cancelled
UI Fixture E2E / ui-fixture-e2e (push) Has been cancelled
UI Fixture E2E / fixture-e2e (push) Has been cancelled
UI Story Gate / story-gate (push) Has been cancelled
vault-ci / test (macos-latest) (push) Has been cancelled
vault-ci / test (ubuntu-latest) (push) Has been cancelled
vault-ci / test (windows-latest) (push) Has been cancelled
vault-ci / app-core wiring tests (push) Has been cancelled
verify-patches / verify patches/CHECKSUMS.sha256 (push) Has been cancelled
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Has been cancelled
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Has been cancelled
Voice Benchmark Smoke / voice bench smoke summary (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Has been cancelled
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Has been cancelled
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Has been cancelled
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Has been cancelled
199 lines
7.1 KiB
Python
199 lines
7.1 KiB
Python
"""Builder for the canonical `eliza_native_v1` corpus record.
|
|
|
|
See `docs/dataset/CANONICAL_RECORD.md`. One row = one Vercel AI SDK
|
|
`generateText` model-call boundary: the exact request and the normalized
|
|
response. This is the only shape new synthesized datasets should emit.
|
|
|
|
Two response shapes occur in the wild:
|
|
|
|
1. **Native tool-call planner output.** `response.text` carries the live
|
|
planner JSON envelope (`{thought, toolCalls:[{id?,name,args}],
|
|
messageToUser?}`) and `response.toolCalls` mirrors the calls in AI-SDK
|
|
form (`{toolCallId, toolName, input}`). Use `native_tool_call_record`.
|
|
2. **Plain structured/handler output.** A single-turn LLM call whose
|
|
response is a JSON object (fact ops, summary, reflection, extracted
|
|
option, ...) or plain assistant text. `response.text` is the verbatim
|
|
model output, no `toolCalls`. Use `native_text_record`.
|
|
|
|
Identity/bookkeeping fields (`trajectoryId`, `agentId`, ...) are optional
|
|
per the canonical doc; synthetic rows carry `metadata` only.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import json
|
|
from typing import Any
|
|
|
|
FORMAT = "eliza_native_v1"
|
|
BOUNDARY_GENERATE_TEXT = "vercel_ai_sdk.generateText"
|
|
SCHEMA_VERSION = 1
|
|
|
|
|
|
def stable_id(*parts: object) -> str:
|
|
h = hashlib.sha256()
|
|
for p in parts:
|
|
h.update(json.dumps(p, sort_keys=True, default=str, ensure_ascii=False).encode("utf-8"))
|
|
h.update(b"\x00")
|
|
return h.hexdigest()[:24]
|
|
|
|
|
|
def _messages(system: str | None, turns: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
out: list[dict[str, Any]] = []
|
|
for t in turns:
|
|
role = t.get("role")
|
|
if role not in {"system", "user", "assistant", "tool"}:
|
|
raise ValueError(f"invalid message role: {role!r}")
|
|
msg: dict[str, Any] = {"role": role, "content": t["content"]}
|
|
if role == "tool" and t.get("tool_call_id"):
|
|
msg["tool_call_id"] = t["tool_call_id"]
|
|
out.append(msg)
|
|
if not any(m["role"] == "user" for m in out):
|
|
raise ValueError("eliza_native_v1 request.messages needs at least one user turn")
|
|
return out
|
|
|
|
|
|
def _base_record(
|
|
*,
|
|
system: str | None,
|
|
messages: list[dict[str, Any]],
|
|
response: dict[str, Any],
|
|
tools: list[dict[str, Any]] | None,
|
|
metadata: dict[str, Any] | None,
|
|
settings: dict[str, Any] | None,
|
|
) -> dict[str, Any]:
|
|
request: dict[str, Any] = {"messages": messages}
|
|
if system:
|
|
request["system"] = system
|
|
if tools:
|
|
request["tools"] = tools
|
|
request["settings"] = settings or {"temperature": 0.0, "topP": 1.0}
|
|
rec: dict[str, Any] = {
|
|
"format": FORMAT,
|
|
"schemaVersion": SCHEMA_VERSION,
|
|
"boundary": BOUNDARY_GENERATE_TEXT,
|
|
"request": request,
|
|
"response": response,
|
|
}
|
|
if metadata:
|
|
rec["metadata"] = metadata
|
|
return rec
|
|
|
|
|
|
def native_text_record(
|
|
*,
|
|
system: str | None,
|
|
user: str | dict[str, Any] | list[dict[str, Any]],
|
|
response_text: str,
|
|
metadata: dict[str, Any] | None = None,
|
|
tools: list[dict[str, Any]] | None = None,
|
|
settings: dict[str, Any] | None = None,
|
|
extra_turns: list[dict[str, Any]] | None = None,
|
|
finish_reason: str = "stop",
|
|
) -> dict[str, Any]:
|
|
"""A single structured/handler-style model call.
|
|
|
|
`user` may be a plain string (one user turn), a single message dict, or
|
|
a full pre-built message list (in which case `extra_turns` is ignored).
|
|
`response_text` is the verbatim model output (JSON object string for
|
|
structured handlers, plain text for replies).
|
|
"""
|
|
if isinstance(user, list):
|
|
turns = list(user)
|
|
else:
|
|
turns = list(extra_turns or [])
|
|
turns.append(user if isinstance(user, dict) else {"role": "user", "content": user})
|
|
messages = _messages(system, turns)
|
|
response = {"text": response_text, "finishReason": finish_reason}
|
|
return _base_record(
|
|
system=system,
|
|
messages=messages,
|
|
response=response,
|
|
tools=tools,
|
|
metadata=metadata,
|
|
settings=settings,
|
|
)
|
|
|
|
|
|
def native_tool_call_record(
|
|
*,
|
|
system: str | None,
|
|
turns: list[dict[str, Any]],
|
|
thought: str,
|
|
tool_calls: list[dict[str, Any]] | None = None,
|
|
message_to_user: str | None = None,
|
|
metadata: dict[str, Any] | None = None,
|
|
tools: list[dict[str, Any]] | None = None,
|
|
settings: dict[str, Any] | None = None,
|
|
) -> dict[str, Any]:
|
|
"""A planner-stage model call whose response is the native planner envelope.
|
|
|
|
`tool_calls` entries are `{name, args, id?}`. The envelope JSON goes in
|
|
`response.text`; `response.toolCalls` carries the AI-SDK mirror.
|
|
"""
|
|
calls = tool_calls or []
|
|
envelope: dict[str, Any] = {"thought": thought, "toolCalls": []}
|
|
sdk_calls: list[dict[str, Any]] = []
|
|
for i, c in enumerate(calls):
|
|
name = c["name"]
|
|
args = c.get("args", {}) or {}
|
|
cid = c.get("id") or f"call_{i}"
|
|
envelope["toolCalls"].append({"id": cid, "name": name, "args": args})
|
|
sdk_calls.append({"toolCallId": cid, "toolName": name, "input": args})
|
|
if message_to_user is not None:
|
|
envelope["messageToUser"] = message_to_user
|
|
response: dict[str, Any] = {
|
|
"text": json.dumps(envelope, ensure_ascii=False, separators=(",", ":")),
|
|
"finishReason": "tool_calls" if sdk_calls else "stop",
|
|
}
|
|
if sdk_calls:
|
|
response["toolCalls"] = sdk_calls
|
|
return _base_record(
|
|
system=system,
|
|
messages=_messages(system, turns),
|
|
response=response,
|
|
tools=tools,
|
|
metadata=metadata,
|
|
settings=settings,
|
|
)
|
|
|
|
|
|
def validate_native_record(rec: dict[str, Any]) -> tuple[bool, str]:
|
|
"""Mirror `format_for_training._format_native_record`'s acceptance gate."""
|
|
if not isinstance(rec, dict):
|
|
return False, "not a dict"
|
|
if rec.get("format") != FORMAT:
|
|
return False, f"format != {FORMAT}"
|
|
if rec.get("boundary") not in {BOUNDARY_GENERATE_TEXT, "vercel_ai_sdk.streamText"}:
|
|
return False, f"bad boundary {rec.get('boundary')!r}"
|
|
req = rec.get("request")
|
|
if not isinstance(req, dict):
|
|
return False, "request not a dict"
|
|
messages = req.get("messages")
|
|
if isinstance(messages, list):
|
|
if not any(isinstance(m, dict) and m.get("role") == "user" for m in messages):
|
|
return False, "request.messages has no user turn"
|
|
elif not isinstance(req.get("prompt"), str) or not req["prompt"].strip():
|
|
return False, "request has neither messages nor prompt"
|
|
resp = rec.get("response")
|
|
if not isinstance(resp, dict):
|
|
return False, "response not a dict"
|
|
has_text = isinstance(resp.get("text"), str) and resp["text"].strip() != ""
|
|
has_calls = isinstance(resp.get("toolCalls"), list) and len(resp["toolCalls"]) > 0
|
|
if not (has_text or has_calls):
|
|
return False, "response has neither text nor toolCalls"
|
|
return True, ""
|
|
|
|
|
|
def write_jsonl(records, path) -> int:
|
|
from pathlib import Path
|
|
|
|
p = Path(path)
|
|
p.parent.mkdir(parents=True, exist_ok=True)
|
|
n = 0
|
|
with p.open("w", encoding="utf-8") as f:
|
|
for r in records:
|
|
f.write(json.dumps(r, ensure_ascii=False) + "\n")
|
|
n += 1
|
|
return n
|