Privoid-ai's picture
Privoid Action-1B experimental preview (LoRA adapter, validator, API, locked test results)
b68d688 verified
|
Raw History Blame Contribute Delete
7.13 kB

Privoid Action — local inference API

A minimal FastAPI service (privoid_action_api/) that serves the Action adapter on the original Granite base, or a validated merged model, from local files only.

Run

cd Privoid-Action-1B
copy api\config.example.json api\config.json      # then set adapter_path to this repository's root folder
python -m privoid_action_api --config api\config.json --check-config
python -m privoid_action_api --config api\config.json
  • Binds to 127.0.0.1:8095 by default. Any non-loopback host is refused unless allow_non_loopback: true is set.
  • Startup sets HF_HUB_OFFLINE=1 / TRANSFORMERS_OFFLINE=1 before any Hugging Face import and loads with local_files_only=True. It never downloads, trains or prepares data. If artifacts are missing or invalid, the server still starts, /health reports model_state: "unavailable", and /extract returns 503.
  • The loader refuses adapters whose base is not Granite 4.0 1B.
  • The model loads at startup (load_on_startup: true). One generation runs at a time (max_concurrent: 1, sized for a 12 GB GPU), with up to max_queue requests waiting.

Endpoints

GET /health

Process health. Never loads or downloads anything.

{"status": "ok", "model_state": "ready", "request_id": "3f1c0a9e5b7d2c41"}

model_state is one of not_loaded, loading, ready, unavailable.

GET /model-info

Model identity (base ID and revision, adapter folder name and a short weight hash), schema version, and limits (max_prompt_tokens, max_new_tokens, max_body_bytes, timeout, concurrency, contract limits). Filesystem paths are not exposed.

POST /extract

Body: one transcript segment in the input schema (docs/SCHEMA.md §1). Example requests are in examples/:

curl.exe -s -X POST http://127.0.0.1:8095/extract -H "content-type: application/json" `
     --data-binary "@examples/request_accepted_request.json"

Response 200 (abridged; the full illustrative response is in examples/response_accepted_request.json. It was produced by the deterministic finalizer from a hand-written output, not by the model):

{
  "request_id": "…", "schema_version": "1.0",
  "result": {
    "schema_version": "1.0", "meeting_id": "meeting-001",
    "actions": [{
      "id": "A1", "task": "Send the revised proposal.", "owner_speaker_ids": ["S2"], "owner_text": "Omar",
      "assignment_status": "accepted", "deadline_text": "Friday", "conditions": [], "status": "active",
      "evidence": [{"turn_id": "T1", "quote": "Omar, can you send the revised proposal by Friday?"},
                   {"turn_id": "T2", "quote": "Yes, I will send it by Friday."}],
      "needs_review": false, "review_reasons": [], "pipeline_flags": ["deadline_not_normalized"],
      "owner_names": ["Omar"], "deadline_normalized": null,
      "deadline_normalization": {"rule": null, "reason": "bare_weekday_ambiguous"}}],
    "decisions": [], "unresolved_requests": [], "rejected_records": []},
  "validation": {"raw_output_fully_valid": true, "rejected_record_count": 0, "grounded_record_count": 0},
  "usage": {"prompt_tokens": 882, "seconds": 1.9},
  "model": {"backend": "transformers", "base_model_id": "ibm-granite/granite-4.0-1b", "…": "…"},
  "notice": "Records are extracted from the transcript for human review. They do not create tasks, …"
}

(The seconds value above is a placeholder, not a measurement.)

POST /extract-meeting (experimental, disabled by default)

Enable with enable_meeting_endpoint: true. The endpoint cuts the meeting into whole-turn segments (max_segment_tokens, segment_overlap_turns), extracts each segment, and reconciles the results with the documented rules (docs/SCHEMA.md §5). The response is marked "experimental": true and includes segments, reconciliation_log, limitations, segment_failures and complete. Its quality is measured separately (notebook §10.5). Cross-segment cancellations are flagged, not applied.

Errors

All errors are JSON: {"error": code, "message": text, "request_id": id, …}. They never contain stack traces or filesystem paths.

Status error When
400 invalid_json Body is not JSON
413 input_too_large Body > max_body_bytes, a contract size limit, or prompt tokens > max_prompt_tokens. Nothing is truncated; split the transcript.
422 invalid_input Schema problems (unknown speaker, duplicate turn ID, invalid date or timezone, control sequences …), listed in problems
429 busy All slots busy and the wait queue is full
502 model_output_invalid The model output was not one schema-valid JSON object. No records are returned and none are invented.
503 model_unavailable Model not loaded (see the server log)
504 timeout Generation exceeded request_timeout_s. Generation is also bounded by max_new_tokens and a matching max_time, and the slot stays occupied until the worker thread really finishes.
500 internal_error Unexpected failure (details only in the server log, keyed by request ID)

A record that fails validation (quote not found, unknown speaker, inconsistent status) is returned in result.rejected_records with its errors, and validation.raw_output_fully_valid is false. Valid records are still returned.

With ground_evidence: true (the default), quotes that the model tidied (fillers or repeated words dropped) or attached to a neighbouring turn are first re-anchored to the transcript's verbatim words; see docs/SCHEMA.md §2.5. Every record changed this way carries a pipeline flag, and validation.grounded_record_count counts them. Set ground_evidence: false to reject such records instead.

Privacy behaviour

  • Transcript bodies are never logged and never written to disk. Log lines carry the request ID, path, status, latency, turn count, prompt-token count and validation counts only. Uvicorn's access log is disabled.
  • No cloud fallback exists. The only network listener is the configured local address.
  • Validation, and any constrained decoding a client might add, ensures the output is well-formed. It is not proof of semantic correctness: owners, statuses and deadlines must still be reviewed by a human before anyone acts on them.
  • Extracted records never send messages, create tasks in external systems, or commit participants to anything.

Privacy of a deployment also depends on the surrounding application, its logs and storage, the host OS and its telemetry, and the network configuration. Those are outside this API.

Tests

python -m unittest discover -s tests -t . runs the API tests against a mocked backend. They cover every route, error code, oversize rejection, invalid-output rejection, record-level rejection, timeouts, the concurrency bound, a full queue (429), hidden internal errors, the loopback-only default, no transcript text in logs, and a check that importing the API does not import torch/transformers/peft/trl. Real-model checks are in notebook §12 (ALLOW_API_CHECKS).