Skip to content

Latest commit

 

History

History
732 lines (606 loc) · 16.8 KB

File metadata and controls

732 lines (606 loc) · 16.8 KB

ORACLE API Reference

Complete API documentation for the ORACLE forecasting system.

Base URL

http://localhost:3000/api

Authentication

Currently, ORACLE uses server-side OpenRouter API keys configured via environment variables. Client-side authentication is not required for local development.

Response Format

All API responses follow a consistent format:

Success Response

{
  "success": true,
  "data": { ... },
  "message": "Optional success message"
}

Error Response

{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable error message",
    "details": { ... }  // Optional additional details
  }
}

Error Codes

Code HTTP Status Description
VALIDATION_ERROR 400 Invalid input data
NOT_FOUND 404 Resource not found
INVALID_OPERATION 400 Operation not allowed
INTERNAL_ERROR 500 Server error

Forecast Endpoints

Create Forecast

Creates a new forecast record.

POST /api/forecast

Request Body

Field Type Required Description
statusQuo string Yes Current state description (50-5000 chars)
resolutionConditions string Yes How the forecast resolves (20-2000 chars)
webResearchModel string Yes Model ID for web research
reasoningModel string Yes Model ID for reasoning
enableBlackSwan boolean No Enable Layer 4 black swan detection (default: true)

Example Request

curl -X POST http://localhost:3000/api/forecast \
  -H "Content-Type: application/json" \
  -d '{
    "statusQuo": "As of December 2024, the Federal Reserve has maintained interest rates at 5.25-5.50% following a series of rate hikes in 2022-2023. Recent inflation data shows CPI at 3.2%, down from peaks of 9.1% in 2022.",
    "resolutionConditions": "This question resolves YES if the Federal Reserve announces a rate cut of at least 25 basis points before June 30, 2025. Resolution based on official FOMC announcements.",
    "webResearchModel": "perplexity/sonar-reasoning-pro",
    "reasoningModel": "google/gemini-3-flash-preview",
    "enableBlackSwan": true
  }'

Success Response (201 Created)

{
  "success": true,
  "id": "cm3abc123def456",
  "status": "PENDING",
  "message": "Forecast created successfully"
}

Validation Errors (400 Bad Request)

{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid input",
    "details": {
      "statusQuo": ["Status quo must be at least 50 characters"],
      "reasoningModel": ["Invalid reasoning model"]
    }
  }
}

Get Forecast

Retrieves a single forecast by ID with all details.

GET /api/forecast/{id}

Path Parameters

Parameter Type Description
id string Forecast ID

Example Request

curl http://localhost:3000/api/forecast/cm3abc123def456

Success Response (200 OK)

{
  "success": true,
  "data": {
    "id": "cm3abc123def456",
    "createdAt": "2024-12-18T10:30:00.000Z",
    "updatedAt": "2024-12-18T10:35:00.000Z",
    "statusQuo": "As of December 2024...",
    "resolutionConditions": "This question resolves YES if...",
    "webResearchModel": "perplexity/sonar-reasoning-pro",
    "reasoningModel": "google/gemini-3-flash-preview",
    "enableBlackSwan": true,
    "status": "COMPLETED",
    "currentLayer": 5,
    "errorMessage": null,
    "decomposition": { ... },
    "swarmEstimates": { ... },
    "debateTranscript": { ... },
    "blackSwans": { ... },
    "calibration": { ... },
    "finalDistribution": {
      "finalDistribution": {
        "pointEstimate": 0.62,
        "ci50": [0.55, 0.69],
        "ci90": [0.42, 0.78],
        "source": "layer5_calibration",
        "swarmMean": 0.58,
        "swarmMedian": 0.60,
        "debateConvergenceScore": 0.85,
        "calibrationApplied": true,
        "methodologySummary": "..."
      }
    },
    "startedAt": "2024-12-18T10:30:05.000Z",
    "completedAt": "2024-12-18T10:35:00.000Z",
    "totalTokensUsed": 87500
  }
}

Not Found (404)

{
  "success": false,
  "error": {
    "code": "NOT_FOUND",
    "message": "Forecast with ID 'invalid-id' not found"
  }
}

List Forecasts

Retrieves a paginated list of forecasts.

GET /api/forecast

Query Parameters

Parameter Type Default Description
limit integer 10 Number of results per page (max 100)
offset integer 0 Number of results to skip
status string all Filter by status: PENDING, RUNNING, COMPLETED, FAILED

Example Request

curl "http://localhost:3000/api/forecast?limit=10&offset=0&status=COMPLETED"

Success Response (200 OK)

{
  "success": true,
  "data": [
    {
      "id": "cm3abc123def456",
      "createdAt": "2024-12-18T10:30:00.000Z",
      "statusQuo": "As of December 2024, the Federal Reserve has maintained...",
      "status": "COMPLETED",
      "currentLayer": 5,
      "pointEstimate": 0.62,
      "ci90": [0.42, 0.78],
      "completedAt": "2024-12-18T10:35:00.000Z"
    },
    {
      "id": "cm3xyz789ghi012",
      "createdAt": "2024-12-17T14:00:00.000Z",
      "statusQuo": "SpaceX has been developing the Starship rocket...",
      "status": "COMPLETED",
      "currentLayer": 5,
      "pointEstimate": 0.45,
      "ci90": [0.28, 0.65],
      "completedAt": "2024-12-17T14:08:00.000Z"
    }
  ],
  "pagination": {
    "total": 25,
    "limit": 10,
    "offset": 0,
    "hasMore": true
  }
}

Start Forecast Processing

Triggers the 5-layer forecasting pipeline for a pending forecast.

POST /api/forecast/{id}/run

Path Parameters

Parameter Type Description
id string Forecast ID

Example Request

curl -X POST http://localhost:3000/api/forecast/cm3abc123def456/run

Success Response (200 OK)

{
  "success": true,
  "message": "Forecast processing started",
  "forecastId": "cm3abc123def456"
}

Already Running (400 Bad Request)

{
  "success": false,
  "error": {
    "code": "INVALID_OPERATION",
    "message": "Forecast is already running or completed"
  }
}

Update Forecast

Updates forecast status and layer outputs (used internally by the pipeline).

PATCH /api/forecast/{id}

Path Parameters

Parameter Type Description
id string Forecast ID

Request Body

Field Type Description
status string PENDING, RUNNING, COMPLETED, FAILED
currentLayer integer Current processing layer (0-5)
errorMessage string Error message if failed
decomposition object Layer 1 output
swarmEstimates object Layer 2 output
debateTranscript object Layer 3 output
blackSwans object Layer 4 output
calibration object Layer 5 output
finalDistribution object Final probability distribution
startedAt string ISO timestamp when processing started
completedAt string ISO timestamp when processing completed
totalTokensUsed integer Total tokens consumed

Example Request

curl -X PATCH http://localhost:3000/api/forecast/cm3abc123def456 \
  -H "Content-Type: application/json" \
  -d '{
    "status": "RUNNING",
    "currentLayer": 2
  }'

Success Response (200 OK)

{
  "success": true,
  "data": {
    "id": "cm3abc123def456",
    "status": "RUNNING",
    "currentLayer": 2,
    "updatedAt": "2024-12-18T10:31:00.000Z"
  }
}

Delete Forecast

Deletes a forecast. Cannot delete forecasts that are currently running.

DELETE /api/forecast/{id}

Path Parameters

Parameter Type Description
id string Forecast ID

Example Request

curl -X DELETE http://localhost:3000/api/forecast/cm3abc123def456

Success Response (200 OK)

{
  "success": true,
  "message": "Forecast deleted successfully"
}

Cannot Delete Running Forecast (400 Bad Request)

{
  "success": false,
  "error": {
    "code": "INVALID_OPERATION",
    "message": "Cannot delete a forecast that is currently running"
  }
}

Models Endpoint

List Available Models

Returns all available AI models for web research and reasoning.

GET /api/models

Example Request

curl http://localhost:3000/api/models

Success Response (200 OK)

{
  "success": true,
  "data": {
    "webResearchModels": [
      {
        "id": "perplexity/sonar-reasoning-pro",
        "name": "Perplexity Sonar Reasoning Pro",
        "description": "Search with reasoning capabilities",
        "provider": "Perplexity",
        "type": "web_research",
        "supportsSystemPrompt": false,
        "supportsReasoning": true,
        "estimatedLatency": "medium",
        "costTier": "high"
      },
      {
        "id": "perplexity/sonar-deep-research",
        "name": "Perplexity Sonar Deep Research",
        "description": "Deep research with comprehensive web search",
        "provider": "Perplexity",
        "type": "web_research",
        "supportsSystemPrompt": false,
        "supportsReasoning": false,
        "estimatedLatency": "slow",
        "costTier": "high"
      }
    ],
    "reasoningModels": [
      {
        "id": "google/gemini-3-flash-preview",
        "name": "Gemini 3 Flash",
        "description": "90% of Gemini 3 Pro capability at 4x lower cost",
        "provider": "Google",
        "type": "reasoning",
        "supportsSystemPrompt": true,
        "supportsReasoning": true,
        "estimatedLatency": "fast",
        "costTier": "low"
      },
      {
        "id": "anthropic/claude-opus-4.5",
        "name": "Claude Opus 4.5",
        "description": "Anthropic's most capable model",
        "provider": "Anthropic",
        "type": "reasoning",
        "supportsSystemPrompt": true,
        "supportsReasoning": true,
        "estimatedLatency": "medium",
        "costTier": "high"
      }
    ],
    "defaults": {
      "webResearchModel": "perplexity/sonar-reasoning-pro",
      "reasoningModel": "google/gemini-3-flash-preview"
    }
  }
}

Data Types

Forecast Status

Status Description
PENDING Created but not yet started
RUNNING Currently processing
COMPLETED Successfully completed
FAILED Processing failed

Layer Outputs

Layer 1: Decomposition

{
  subquestions: Array<{
    question: string;
    category: "historical" | "current" | "mechanism" | "assumption";
    importance: "high" | "medium" | "low";
  }>;
  referenceClasses: Array<{
    name: string;
    description: string;
    baseRate: number;
    relevance: "high" | "medium" | "low";
    sampleSize?: number;
    sources: string[];
  }>;
  synthesizedBaseRate: {
    estimate: number;
    confidence: "high" | "medium" | "low";
    reasoning: string;
  };
  researchFindings: Array<{
    query: string;
    findings: string;
    relevance: "high" | "medium" | "low";
  }>;
}

Layer 2: Swarm Estimates

{
  agents: Array<{
    id: string;
    name: string;
    persona: string;
    estimate: number;
    confidence: "high" | "medium" | "low";
    reasoning: string;
    keyFactors: string[];
  }>;
  aggregateStats: {
    mean: number;
    median: number;
    standardDeviation: number;
    range: [number, number];
  };
}

Layer 3: Debate Transcript

{
  rounds: Array<{
    roundNumber: number;
    exchanges: Array<{
      agentId: string;
      position: "for" | "against";
      argument: string;
      evidence: string[];
      rebuttal?: string;
    }>;
  }>;
  convergenceMetrics: {
    initialVariance: number;
    finalVariance: number;
    convergenceScore: number;
    convergenceInterpretation: "strong_convergence" | "moderate_convergence" | "no_change" | "divergence";
  };
  postDebateEstimates: Array<{
    agentId: string;
    previousEstimate: number;
    newEstimate: number;
    reasoning: string;
  }>;
}

Layer 4: Black Swan Scenarios

{
  scenarios: Array<{
    id: string;
    description: string;
    probability: number;
    impactMagnitude: number;
    impactDirection: "increase" | "decrease" | "invalidate";
    category: string;
    researchFindings: string;
  }>;
  integrationRecommendations: {
    highImpactScenarios: Array<{ scenarioId: string; adjustment: number }>;
    tailDistributionAdjustments: Array<{ direction: string; adjustment: number }>;
  };
}

Layer 5: Calibration Output

{
  finalDistribution: {
    pointEstimate: number;
    ci50: [number, number];
    ci90: [number, number];
    calibrationApplied: boolean;
    extremizationApplied: boolean;
    tailIntegrationApplied: boolean;
  };
  temperatureScaling: {
    temperature: number;
    adjusted: boolean;
  };
  extremization: {
    extremizationD: number;
    applied: boolean;
  };
  qualityMetrics: {
    agentAgreement: number;
    calibrationConfidence: "high" | "medium" | "low";
  };
  betaDistribution: {
    alpha: number;
    beta: number;
    points: Array<{ x: number; y: number }>;
  };
  percentiles: {
    p5: number;
    p10: number;
    p25: number;
    p50: number;
    p75: number;
    p90: number;
    p95: number;
  };
  methodologySummary: string;
  keyFactors: string[];
  uncertaintySources: string[];
  caveats: string[];
}

WebSocket Events (Future)

Real-time progress updates are planned for future releases using WebSocket connections.


Rate Limits

Rate limits are determined by your OpenRouter API key tier. The ORACLE application does not impose additional rate limits, but implements:

  • Automatic retry with exponential backoff
  • Request caching to reduce duplicate API calls
  • Configurable concurrency limits

Examples

Complete Workflow Example

# 1. Create a forecast
FORECAST_ID=$(curl -s -X POST http://localhost:3000/api/forecast \
  -H "Content-Type: application/json" \
  -d '{
    "statusQuo": "Tesla stock (TSLA) is trading at $250 as of December 2024. The company reported Q3 2024 earnings with revenue of $25.5 billion and vehicle deliveries of 462,000 units.",
    "resolutionConditions": "Resolves YES if TSLA closes above $300 on any trading day before March 31, 2025. Based on official NASDAQ closing prices.",
    "webResearchModel": "perplexity/sonar-reasoning-pro",
    "reasoningModel": "google/gemini-3-flash-preview"
  }' | jq -r '.id')

echo "Created forecast: $FORECAST_ID"

# 2. Start processing
curl -X POST "http://localhost:3000/api/forecast/$FORECAST_ID/run"

# 3. Poll for completion
while true; do
  STATUS=$(curl -s "http://localhost:3000/api/forecast/$FORECAST_ID" | jq -r '.data.status')
  LAYER=$(curl -s "http://localhost:3000/api/forecast/$FORECAST_ID" | jq -r '.data.currentLayer')
  echo "Status: $STATUS, Layer: $LAYER"

  if [ "$STATUS" = "COMPLETED" ] || [ "$STATUS" = "FAILED" ]; then
    break
  fi

  sleep 10
done

# 4. Get final results
curl -s "http://localhost:3000/api/forecast/$FORECAST_ID" | jq '.data.finalDistribution'

Python Client Example

import requests
import time

BASE_URL = "http://localhost:3000/api"

def create_forecast(status_quo: str, resolution: str) -> str:
    response = requests.post(f"{BASE_URL}/forecast", json={
        "statusQuo": status_quo,
        "resolutionConditions": resolution,
        "webResearchModel": "perplexity/sonar-reasoning-pro",
        "reasoningModel": "google/gemini-3-flash-preview",
        "enableBlackSwan": True
    })
    response.raise_for_status()
    return response.json()["id"]

def start_forecast(forecast_id: str) -> None:
    response = requests.post(f"{BASE_URL}/forecast/{forecast_id}/run")
    response.raise_for_status()

def get_forecast(forecast_id: str) -> dict:
    response = requests.get(f"{BASE_URL}/forecast/{forecast_id}")
    response.raise_for_status()
    return response.json()["data"]

def wait_for_completion(forecast_id: str, timeout: int = 600) -> dict:
    start = time.time()
    while time.time() - start < timeout:
        forecast = get_forecast(forecast_id)
        if forecast["status"] == "COMPLETED":
            return forecast
        if forecast["status"] == "FAILED":
            raise Exception(f"Forecast failed: {forecast['errorMessage']}")
        print(f"Layer {forecast['currentLayer']}/5...")
        time.sleep(10)
    raise TimeoutError("Forecast timed out")

# Usage
forecast_id = create_forecast(
    "The 2024 US presidential election...",
    "Resolves YES if Democratic candidate wins..."
)
start_forecast(forecast_id)
result = wait_for_completion(forecast_id)
print(f"Probability: {result['finalDistribution']['finalDistribution']['pointEstimate']:.1%}")