Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions plutus_agent/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ def micros_to_usd(micros) -> float:
"context_render_hash",
"served_memory_provenance_hash",
"action_receipt_hash",
"resource_constraints_version",
"resource_constraints_hash",
)


Expand Down Expand Up @@ -568,6 +570,8 @@ def verify_checkpoints(conn, checkpoints, hmac_key: Optional[bytes] = None) -> d
context_render_hash TEXT,
served_memory_provenance_hash TEXT,
action_receipt_hash TEXT,
resource_constraints_version TEXT,
resource_constraints_hash TEXT,
estimated INTEGER NOT NULL DEFAULT 1,
source TEXT NOT NULL DEFAULT 'api',
ts REAL NOT NULL,
Expand Down Expand Up @@ -880,6 +884,8 @@ def _migrate_add_columns(conn) -> None:
("usage_events", "context_render_hash", "TEXT"),
("usage_events", "served_memory_provenance_hash", "TEXT"),
("usage_events", "action_receipt_hash", "TEXT"),
("usage_events", "resource_constraints_version", "TEXT"),
("usage_events", "resource_constraints_hash", "TEXT"),
]
for table, col, defn in additions:
cols = _table_columns(conn, table)
Expand Down
14 changes: 12 additions & 2 deletions plutus_agent/metering.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ def record_usage(conn, org_id: str, provider: str,
context_render_hash: Optional[str] = None,
served_memory_provenance_hash: Optional[str] = None,
action_receipt_hash: Optional[str] = None,
resource_constraints_version: Optional[str] = None,
resource_constraints_hash: Optional[str] = None,
user_id: Optional[str] = None, source: str = "api",
pricing_overrides: Optional[dict] = None,
ts: Optional[float] = None,
Expand Down Expand Up @@ -255,6 +257,11 @@ def record_usage(conn, org_id: str, provider: str,
served_memory_provenance_hash = (served_memory_provenance_hash.lower()
if served_memory_provenance_hash else None)
action_receipt_hash = action_receipt_hash.lower() if action_receipt_hash else None
if resource_constraints_version is not None and not isinstance(resource_constraints_version, str):
raise ValueError("resource_constraints_version must be a string")
if resource_constraints_hash is not None and not _SHA256_HEX.fullmatch(resource_constraints_hash):
raise ValueError("resource_constraints_hash must be a 64-character SHA-256 hex digest")
resource_constraints_hash = resource_constraints_hash.lower() if resource_constraints_hash else None

# Fix #80: never let a negative token count through — it would rewind the
# month-to-date tracked total (bypassing the free-tier quota) and corrupt
Expand Down Expand Up @@ -466,6 +473,8 @@ def record_usage(conn, org_id: str, provider: str,
"context_render_hash": context_render_hash,
"served_memory_provenance_hash": served_memory_provenance_hash,
"action_receipt_hash": action_receipt_hash,
"resource_constraints_version": resource_constraints_version,
"resource_constraints_hash": resource_constraints_hash,
}
row_hash = db.compute_row_hash(prev_hash, row_fields, hmac_key=chain_hmac_key)
conn.execute(
Expand All @@ -474,8 +483,8 @@ def record_usage(conn, org_id: str, provider: str,
"baseline_micros,optimal_micros,external_ref,user_id,evidence_hashes,policy_version,"
"result_hash,human_review,correction_ref,agent_id,authority_manifest_ref,scope_anchor,"
"action_intent_hash,action_status,approval_ref,context_render_schema,context_render_hash,"
"served_memory_provenance_hash,action_receipt_hash,estimated,source,ts,prev_hash,row_hash) "
"VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
"served_memory_provenance_hash,action_receipt_hash,resource_constraints_version,resource_constraints_hash,estimated,source,ts,prev_hash,row_hash) "
"VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
(eid, org_id, workspace_id, provider, model, task_type,
int(input_tokens), int(output_tokens), int(cache_read_tokens),
(int(cache_write_tokens) if cache_write_tokens is not None else None),
Expand All @@ -484,6 +493,7 @@ def record_usage(conn, org_id: str, provider: str,
human_review, correction_ref, agent_id, authority_manifest_ref, scope_anchor,
action_intent_hash, action_status, approval_ref, context_render_schema,
context_render_hash, served_memory_provenance_hash, action_receipt_hash,
resource_constraints_version, resource_constraints_hash,
int(estimated), source, ts, prev_hash, row_hash),
)

Expand Down
3 changes: 3 additions & 0 deletions plutus_agent/server/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ def audit_json(conn, org_id: str, *, hmac_key: bytes | None = None,
"action_intent_hash": row["action_intent_hash"],
"status": row["action_status"],
"approval_ref": row["approval_ref"],
**({"resource_constraints_version": row["resource_constraints_version"],
"resource_constraints_hash": row["resource_constraints_hash"]}
if row["resource_constraints_version"] is not None or row["resource_constraints_hash"] is not None else {}),
},
"resource_allocation": {
"input_tokens": row["input_tokens"],
Expand Down
5 changes: 4 additions & 1 deletion plutus_agent/server/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -826,7 +826,8 @@ def _ingest_usage(self, conn):
"agent_id", "authority_manifest_ref", "scope_anchor",
"action_intent_hash", "action_status", "approval_ref",
"context_render_schema", "context_render_hash",
"served_memory_provenance_hash", "action_receipt_hash"):
"served_memory_provenance_hash", "action_receipt_hash",
"resource_constraints_version", "resource_constraints_hash"):
if ev.get(field) is not None and not isinstance(ev[field], str):
return self._json(400, {"error": f"{field} must be a string"})

Expand Down Expand Up @@ -890,6 +891,8 @@ def _ingest_usage(self, conn):
context_render_hash=ev.get("context_render_hash"),
served_memory_provenance_hash=ev.get("served_memory_provenance_hash"),
action_receipt_hash=ev.get("action_receipt_hash"),
resource_constraints_version=ev.get("resource_constraints_version"),
resource_constraints_hash=ev.get("resource_constraints_hash"),
user_id=ev.get("user_id"),
source=ev.get("source", "api"),
pricing_overrides=cfg.get("pricing", {}).get("overrides"),
Expand Down
Loading
Loading