Sign inSign up

chiefj/perplexity

By chiefj

Updated 4 months ago

Perplexity scoring microservice

Image
Developer tools
0

634

chiefj/perplexity repository overview

Perplexity Scorer

Layer 3 perplexity scoring microservice — Score 4 in the LLM Observability composite quality index. Runs on port 8007. Zero GPU required.


Table of Contents

  1. What It Does
  2. Run Locally (Python)
  3. Run with Docker
  4. Run with Docker Compose
  5. API Reference
  6. Skip Conditions
  7. Baselines & Alerting
  8. Composite Weight
  9. Environment Variables
  10. CI / CD
  11. Algorithm

What It Does

Scores the cross-entropy perplexity of an LLM response.

Scorer pathWhen usedCost
Provider logprobs (primary)token_logprobs array providedZero inference
GPT-2 124M ONNX (fallback)No logprobs supplied~25ms on CPU

Returns null (skipped) when skip conditions are met. The composite weight drops to 0.00 automatically.


Run Locally (Python)

cd packages/python/perplexity

pip install -e ".[dev]"

SKIP_CONSOLE_EXPORTER=true SKIP_OTLP_EXPORTER=true \
  pytest tests/ --cov=src -v

Start the server:

PYTHONPATH=src uvicorn api.rest.v1.app:app --host 0.0.0.0 --port 8007 --reload

Run with Docker

Pull and run the published image
docker pull chiefj/perplexity:latest

docker run -d \
  --name perplexity \
  -p 8007:8007 \
  -e SKIP_OTLP_EXPORTER=true \
  chiefj/perplexity:latest
Health check
curl http://localhost:8007/health
# {"status":"ok","scorer":"provider_logprobs"}
Score a response (with provider logprobs)
curl -X POST http://localhost:8007/v1/score/perplexity \
  -H "Content-Type: application/json" \
  -d '{
    "trace_id": "abc123",
    "span_id": "def456",
    "response_text": "The mitochondria is the powerhouse of the cell.",
    "completion_tokens": 12,
    "prompt_type": "chat",
    "token_logprobs": [-1.2, -0.9, -1.5, -2.1, -0.8, -1.3, -1.0, -0.7, -1.4, -2.0, -1.1, -0.6]
  }'

Expected response:

{
  "trace_id": "abc123",
  "span_id": "def456",
  "perplexity": 3.31,
  "score": 0.72,
  "weight": 0.10,
  "skipped": false,
  "skip_reason": null,
  "high_perplexity_flag": false,
  "prompt_type": "chat",
  "scorer_used": "provider_logprobs"
}
Score a response (GPT-2 fallback — no logprobs)
curl -X POST http://localhost:8007/v1/score/perplexity \
  -H "Content-Type: application/json" \
  -d '{
    "trace_id": "xyz789",
    "span_id": "qrs012",
    "response_text": "Paris is the capital of France and a major European city.",
    "completion_tokens": 15,
    "prompt_type": "rag"
  }'
Skipped response (too few tokens)
curl -X POST http://localhost:8007/v1/score/perplexity \
  -H "Content-Type: application/json" \
  -d '{
    "trace_id": "skip1",
    "span_id": "skip2",
    "response_text": "Yes.",
    "completion_tokens": 2,
    "prompt_type": "chat"
  }'
# {"skipped": true, "skip_reason": "completion_tokens_too_few", "weight": 0.0, ...}
Build the image locally
cd packages/python/perplexity

docker build \
  -f build/Dockerfile \
  -t chiefj/perplexity:local \
  .

Run with Docker Compose

cd packages/python/perplexity

docker compose -f deploy/docker/docker-compose.yaml up -d

Stop:

docker compose -f deploy/docker/docker-compose.yaml down

API Reference

Full contract: contracts/openapi/v1.yaml

POST /v1/score/perplexity
FieldTypeRequiredDescription
trace_idstringDistributed trace ID
span_idstringParent span ID
response_textstringLLM completion text
completion_tokensintToken count — < 10 triggers skip
prompt_typeenumchat | code | rag | classification
token_logprobsfloat[]Provider log-probs (primary path)
finish_reasonstringcontent_filter triggers skip
GET /health

Returns {"status": "ok", "scorer": "<provider_logprobs|gpt2_onnx|unavailable>"}.


Skip Conditions

Conditionskip_reasonweight
completion_tokens < 10completion_tokens_too_few0.00
finish_reason == "content_filter"finish_reason_blocked0.00
No logprobs AND GPT-2 unavailablescorer_unavailable0.00

Baselines & Alerting

prompt_typeExpected perplexityHIGH flag threshold (3 ×)
chat15–35> 105
code8–20> 60
rag12–28> 84
classification6–15> 45

high_perplexity_flag: true indicates the model was likely confused, hallucinating, or output low-quality text.


Composite Weight

Statew_perplexityscore contribution
perplexity available0.101/log(perplexity) → normalized to [0, 1]
perplexity skipped0.00other scorer weights renormalized externally

Environment Variables

VariableDefaultDescription
GPT2_MODEL_PATHgpt2HuggingFace model ID or local path for GPT-2 ONNX
SKIP_CONSOLE_EXPORTERfalseSuppress OTel console span output
SKIP_OTLP_EXPORTERtrueDisable OTLP gRPC/HTTP exporter
OTEL_EXPORTER_OTLP_ENDPOINThttp://localhost:4317OTLP collector endpoint
DEPLOYMENT_ENVdevOTel resource deployment.env attribute

Copy .env.example to .env to configure locally.


CI / CD

The pipeline (perplexity-ci.yml) triggers only when files under packages/python/perplexity/ change and the target branch is main:

EventJobs triggered
Pull request → main (perplexity path)test (Python 3.11 + 3.12)
Push/merge → main (perplexity path)testbuild-and-push
GitHub Secrets required

Go to Settings → Secrets → Actions and add:

Secret nameValue
DOCKERHUB_USERNAMEchiefj
DOCKERHUB_TOKENyour Docker Hub access token
Docker Hub tags published on merge to main
chiefj/perplexity:latest
chiefj/perplexity:stable
chiefj/perplexity:v<version>

Algorithm

Primary path (provider logprobs):
  perplexity = exp( -1/N × Σ token_logprobs )

Fallback path (GPT-2 124M ONNX, CPU):
  perplexity = exp( cross_entropy_loss )   # ~25ms / 200 tokens

Normalized score:
  contribution = 1 / log(perplexity)
  score        = clamp((contribution - low) / (high - low), 0, 1)

Tag summary

Content type

Image

Digest

sha256:1790fab76

Size

109.4 MB

Last updated

4 months ago

docker pull chiefj/perplexity