Python API

The Python FastAPI backend provides full access to the ACP consensus engine for local development. It supports both asynchronous (task-based) and synchronous consensus modes. Base URL: http://localhost:8000

Starting the server

Run the FastAPI server with:

uvicorn main:app --reload --port 8000

Endpoint Overview

MethodPathDescription
POST/consensusCreate async consensus task (returns task ID for polling)
GET/consensus/{id}Poll status and result of a consensus task
POST/consensus/syncSynchronous consensus (blocks until complete)
GET/healthHealth check

ConsensusRequest Schema

Both POST /consensus and POST /consensus/sync accept the same request body:

FieldTypeRequiredDefaultDescription
querystringYes--The question or prompt to reach consensus on
modelsModelConfig[]Yes--Array of model configurations (at least 2)
max_iterationsintegerNo7Maximum number of phi-spiral iterations
structurestringNo"sonata"Musical structure: "fugue" (deep analysis), "sonata" (conflict resolution), "concert" (creative)

ModelConfig Object

FieldTypeRequiredDescription
providerstringYesLLM provider: "openrouter", "openai", "anthropic"
namestringYesHuman-readable model name (e.g., "gpt4", "claude")
modelstringYesFull model identifier (e.g., "openai/gpt-5.4-mini")
temperaturefloatNoSampling temperature (default: 0.7)
Full request example
{
  "query": "What is 2+2?",
  "models": [
    {
      "provider": "openrouter",
      "name": "gpt4",
      "model": "openai/gpt-5.4-mini",
      "temperature": 0.7
    },
    {
      "provider": "openrouter",
      "name": "claude",
      "model": "anthropic/claude-haiku-4-5",
      "temperature": 0.7
    }
  ],
  "max_iterations": 7,
  "structure": "sonata"
}

POST /consensus -- Async Consensus

Creates a consensus task that runs in the background. Returns immediately with a task ID that you can poll for status and results. Ideal for long-running queries or when you want non-blocking behavior.

Request
curl -X POST http://localhost:8000/consensus \
  -H "Content-Type: application/json" \
  -d '{
    "query": "What is 2+2?",
    "models": [
      {"provider": "openrouter", "name": "gpt4", "model": "openai/gpt-5.4-mini"},
      {"provider": "openrouter", "name": "claude", "model": "anthropic/claude-haiku-4-5"}
    ],
    "max_iterations": 5,
    "structure": "sonata"
  }'
Response 202
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "pending",
  "progress": 0.0
}
FieldTypeDescription
idstring (UUID)Unique task identifier for polling
statusstringTask status: "pending", "running", "completed", or "failed"
progressfloatProgress from 0.0 to 1.0

GET /consensus/{id} -- Poll Task Status

Poll a consensus task by its ID. When the task is still running, the response includes progress. When complete, the full result is included.

Request
curl http://localhost:8000/consensus/550e8400-e29b-41d4-a716-446655440000
Response 200 (running)
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "running",
  "progress": 0.4
}
Response 200 (completed)
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "completed",
  "progress": 1.0,
  "result": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "query": "What is 2+2?",
    "final_answer": "4",
    "consensus_reached": true,
    "iterations_used": 1,
    "final_D": 0.0,
    "final_H_total": 1.0,
    "iterations": []
  }
}

Polling strategy

Poll every 1-2 seconds. Check the progress field to estimate remaining time. A progress of 0.4 means roughly 40% of iterations have completed.

ConsensusResponse Schema

When a task completes, the result field contains the full consensus response. The same schema is returned directly by the synchronous endpoint.

FieldTypeDescription
idstring (UUID)Consensus task identifier
querystringThe original input query
final_answerstringThe synthesized consensus answer
consensus_reachedbooleanWhether models converged to agreement
iterations_usedintegerNumber of phi-spiral iterations performed
final_DfloatFinal divergence score (0 = perfect consensus)
final_H_totalfloatFinal harmony score (H = 1 - D)
iterationsarrayDetailed data for each iteration (responses, D-scores, axioms used)

POST /consensus/sync -- Synchronous Consensus

Runs consensus synchronously and blocks until the result is available. The request body is the same as the async endpoint, but the response returns the full ConsensusResponse directly (no polling required).

Timeout considerations

Synchronous consensus can take 10-60 seconds depending on model count, iteration depth, and model latency. Make sure your HTTP client timeout is set accordingly. For long-running queries, prefer the async endpoint.

Request
curl -X POST http://localhost:8000/consensus/sync \
  -H "Content-Type: application/json" \
  -d '{
    "query": "What is 2+2?",
    "models": [
      {"provider": "openrouter", "name": "gpt4", "model": "openai/gpt-5.4-mini"},
      {"provider": "openrouter", "name": "claude", "model": "anthropic/claude-haiku-4-5"}
    ],
    "max_iterations": 5
  }'
Response 200
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "query": "What is 2+2?",
  "final_answer": "4",
  "consensus_reached": true,
  "iterations_used": 1,
  "final_D": 0.0,
  "final_H_total": 1.0,
  "iterations": []
}

GET /health -- Health Check

Returns the service status. Use for readiness probes and uptime monitoring.

Request
curl http://localhost:8000/health
Response 200
{
  "status": "healthy",
  "service": "ACP Python API",
  "version": "4.0.0"
}

Async Workflow Example

A complete example showing the create-then-poll pattern:

Step 1: Create task
TASK_ID=$(curl -s -X POST http://localhost:8000/consensus \
  -H "Content-Type: application/json" \
  -d '{
    "query": "Explain quantum entanglement",
    "models": [
      {"provider": "openrouter", "name": "gpt4", "model": "openai/gpt-5.4-mini"},
      {"provider": "openrouter", "name": "claude", "model": "anthropic/claude-haiku-4-5"}
    ]
  }' | jq -r '.id')

echo "Task created: $TASK_ID"
Step 2: Poll until complete
while true; do
  STATUS=$(curl -s http://localhost:8000/consensus/$TASK_ID)
  STATE=$(echo $STATUS | jq -r '.status')
  PROGRESS=$(echo $STATUS | jq -r '.progress')

  echo "Status: $STATE  Progress: $PROGRESS"

  if [ "$STATE" = "completed" ] || [ "$STATE" = "failed" ]; then
    echo $STATUS | jq '.result'
    break
  fi

  sleep 2
done