Skip to content
Open
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
__pycache__/
*.py[cod]
data/
62 changes: 62 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,46 @@ curl -X POST http://localhost:8080/api/repurpose \
curl http://localhost:8080/api/usage -H "X-API-Key: cs_your_key"
```

### Score Submission Quality
```bash
curl -X POST http://localhost:8080/api/quality-score \
-H "X-API-Key: cs_your_key" \
-H "Content-Type: application/json" \
-d '{
"submission": {
"title": "Launch checklist",
"summary": "A clear release checklist for a small SaaS launch.",
"steps": ["validate metrics", "publish docs", "notify customers"]
},
"rubric": {
"required_fields": ["title", "summary", "steps"],
"required_keywords": ["release", "customers"],
"expected_format": "json",
"min_words": 10,
"pass_threshold": 0.7
}
}'
```

The quality scorer auto-detects JSON, markdown, code, and plain text submissions. It returns:

```json
{
"weighted_score": 0.94,
"quality_rating": "excellent",
"scores": {
"completeness": 1.0,
"format_compliance": 1.0,
"coverage": 1.0,
"clarity": 0.75,
"validity": 1.0
},
"feedback": ["Detected submission format: json."],
"pass_threshold": true,
"detected_format": "json"
}
```

## 🎯 Platforms

| Platform | Output |
Expand All @@ -48,6 +88,28 @@ curl http://localhost:8080/api/usage -H "X-API-Key: cs_your_key"
| `video_script` | 60s script with B-roll suggestions |
| `summary` | 2-3 sentence summary |

## Quality Scoring

`quality_scoring.py` implements deterministic 0-1 scoring across five weighted dimensions:

| Dimension | Weight |
|-----------|--------|
| Completeness | 0.30 |
| Format Compliance | 0.20 |
| Coverage | 0.25 |
| Clarity | 0.15 |
| Validity | 0.10 |

The scorer supports optional rubric keys:

| Key | Purpose |
|-----|---------|
| `required_fields` | Required JSON keys or text labels |
| `required_keywords` | Required coverage terms |
| `min_words` | Completeness length baseline |
| `expected_format` | One of `json`, `markdown`, `code`, `text` |
| `pass_threshold` | Minimum weighted score for pass/fail |

## 💰 Pricing

| Plan | Price | Requests/mo | Platforms |
Expand Down
30 changes: 29 additions & 1 deletion app.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@
import json
from datetime import datetime
from pathlib import Path
from typing import Optional
from typing import Any, Optional

from fastapi import FastAPI, HTTPException, Depends, Header
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import HTMLResponse
from pydantic import BaseModel, Field

from middleware import validate_api_key, track_usage, get_usage_stats, get_or_create_key, PLANS
from quality_scoring import score_submission

app = FastAPI(
title="ContentSplit",
Expand Down Expand Up @@ -57,6 +58,26 @@ class RepurposeResponse(BaseModel):
created_at: str


class QualityScoreRequest(BaseModel):
submission: Any = Field(..., description="Submission content as JSON, markdown, code, or text")
rubric: dict[str, Any] = Field(
default_factory=dict,
description=(
"Optional scoring rubric. Supported keys: required_fields, "
"required_keywords, min_words, expected_format, pass_threshold"
),
)


class QualityScoreResponse(BaseModel):
weighted_score: float
quality_rating: str
scores: dict[str, float]
feedback: list[str]
pass_threshold: bool
detected_format: str


# ── Content Generation (using prompts, model-agnostic) ────────────────────

PLATFORM_PROMPTS = {
Expand Down Expand Up @@ -376,6 +397,13 @@ async def repurpose_content(req: RepurposeRequest, user: dict = Depends(validate
)


@app.post("/api/quality-score", response_model=QualityScoreResponse)
async def quality_score(req: QualityScoreRequest, user: dict = Depends(validate_api_key)):
"""Score structured submissions against a configurable rubric."""
track_usage(user.get("key", "anonymous"))
return score_submission(req.submission, req.rubric)


@app.get("/api/platforms")
async def list_platforms():
"""List available target platforms."""
Expand Down
Loading