Skip to content

Latest commit

 

History

History
266 lines (201 loc) · 9.12 KB

API.md

File metadata and controls

266 lines (201 loc) · 9.12 KB

API Reference

Base URL: http://localhost:8000 — interactive docs at /docs (FastAPI auto-generated). CORS is restricted to http://localhost:5173 and http://127.0.0.1:5173 for GET, POST and OPTIONS.

Endpoint overview

Method Path Purpose Egress
GET /api/health Liveness probe none
POST /api/analysis Local document metrics none
POST /api/profiles/analyze Pool samples into a style profile none
POST /api/profiles/compare Compare a document to a profile none
POST /api/style/compare Advanced style-fit comparison none
POST /api/issues/analyze Build a problem map none
GET /api/suggestions/status Is a provider configured? none
POST /api/suggestions Targeted alternative wordings provider
POST /api/suggestions/validate Validate a replacement locally none

Only one endpoint can contact an external provider, and only after an explicit user action. Every other endpoint is fully local.

Error contract

Unhandled errors return a structured 500 — never a traceback or document text:

{ "detail": { "code": "INTERNAL_ERROR", "message": "The server could not complete this request." } }

Suggestion endpoints return typed errors using the same envelope:

Code HTTP Meaning
PROVIDER_NOT_CONFIGURED 503 No provider configured — local features still work
PROVIDER_AUTH_FAILED 502 Provider rejected the API key
PROVIDER_RATE_LIMITED 429 Provider rate limit (never retried)
PROVIDER_TIMEOUT 504 Provider did not respond in time (never retried)
PROVIDER_UNAVAILABLE 502 Transient provider failure after one retry
INVALID_PROVIDER_RESPONSE 502 Response could not be parsed into a valid shape
INVALID_REQUEST 422 Missing or malformed field for the chosen mode
STALE_ANALYSIS 409 Supplied text no longer matches the issue offsets
STALE_SELECTION 409 Selection offsets no longer match the supplied text
INVALID_TARGET_RANGE 422 Offsets fall outside the text
SELECTION_TOO_LONG 422 Selection exceeds the per-request target limit
UNSUPPORTED_ISSUE_SCOPE 422 A DOCUMENT-scope issue cannot target one passage

Schema violations return FastAPI's standard 422 with a detail array.


GET /api/health

{ "status": "ok" }

POST /api/analysis

Analyze text locally. Whitespace-only input is rejected with 422. Document size is capped at 200,000 characters.

Request

{ "text": "Your document text." }

Response — a typed, logically grouped tree (see METRICS.md for every field's definition):

{
  "document":   { "char_count": 0, "word_count": 0, "...": "..." },
  "sentences":  { "average": 0.0, "population_stddev": 0.0, "...": "..." },
  "paragraphs": { "...": "..." },
  "lexical":    { "type_token_ratio": 0.0, "root_type_token_ratio": 0.0 },
  "repetition": { "bigrams": [], "trigrams": [], "highlights": [] },
  "transitions": [],
  "sentence_openings": [],
  "punctuation": { "raw": {}, "per_100_words": {} },
  "contractions": { "count": 0, "per_100_words": 0.0 },
  "readability": { "flesch_reading_ease": null, "flesch_kincaid_grade": null }
}

Fields that are undefined for degenerate input (for example an average with no words) are null, never NaN.

POST /api/profiles/analyze

Pool writing samples into a personal style profile. Requires 2–12 non-blank samples, at most 60,000 characters each and 200,000 characters in total.

Request

{ "samples": [ { "label": "Essay", "text": "..." }, { "text": "..." } ] }

Response — PersonalStyleProfile: coverage, reliability (VERY_LOW / LOW / MODERATE / HIGH), pooled sentence, paragraph and lexical style, per-feature stability across samples, language_habits and opening_patterns. From the advanced-style layer it may also carry an advanced block. Profiles built before that layer existed simply omit it and are supported by legacy derivation.

Profiles are returned to the caller and kept in frontend memory only — there is no persistence and no account.

POST /api/profiles/compare

Compare one document against an existing profile.

Request

{ "text": "...", "profile": { "coverage": {}, "reliability": {} } }

Response — ComparisonResult: per-metric differences plus the normalized style distance. This is a measurement only — never a human/AI classification.

POST /api/style/compare

Advanced style comparison: one text against a profile.

Request

{ "text": "...", "profile": { "...": "..." } }

Response — StyleFitResult: a reliability-weighted normalized style distance (lower means closer to the profile), a confidence level (HIGH / MODERATE / LIMITED) and the strongest explainable deviations. When evidence is weak the result is reported as LIMITED_EVIDENCE rather than a confident label. Measurement only — never a probability, human score or authorship claim.

POST /api/issues/analyze

Build a problem map. The profile is optional: repetition detectors run without one, while profile-deviation detectors need it.

Request

{ "text": "...", "profile": null }

Response — ProblemMap, a list of explainable issues. Each issue carries an id, type, scope, severity, character_start / character_end (UTF-16 code units), an excerpt taken verbatim from the source text, and a human-readable explanation.

Issue types — SENTENCE_LENGTH_DEVIATION, REPEATED_SENTENCE_OPENING, LOCAL_SENTENCE_LENGTH_MONOTONY, TRANSITION_OVERUSE, LOCAL_PHRASE_REPETITION, PARAGRAPH_LENGTH_DEVIATION, PARAGRAPH_SENTENCE_DENSITY, PUNCTUATION_STYLE_DEVIATION.

Scopes — SENTENCE, PARAGRAPH, LOCAL_PATTERN, DOCUMENT. Only the first three can target a passage for suggestions.

Issues are capped per type and grouped so the map stays readable. A map is never a detector and never an automatic edit.

GET /api/suggestions/status

Whether the optional provider is configured. Never exposes keys, base URLs or model credentials.

POST /api/suggestions

Generate 1–3 targeted alternative wordings for one selected passage. Requires an explicitly configured provider; otherwise 503.

Two entry paths share one pipeline, selected by mode:

  • "issue" (analyzer-guided) — the backend re-derives the target passage from the supplied text and the issue's offsets, never trusting a client-side excerpt alone. Mismatches are rejected as STALE_ANALYSIS.
  • "selection" (manual) — the user selected text directly in the editor. No analysis, problem map or issue is required. The backend re-slices the text at the exact captured offsets and requires an exact match with the client's selected_text, otherwise STALE_SELECTION.

Request

{
  "text": "...",
  "mode": "selection",
  "issue": null,
  "selection": { "start": 10, "end": 48, "selected_text": "..." },
  "protected_terms": ["Acme Corp"],
  "goal": "tighten this sentence"
}

Response — SuggestionResponse: mode, issue_id (null in selection mode), suggestions, the verified target_start / target_end, provider, model, generated_at, warnings, and protected_items. Provider and model identifiers are descriptive only — no secrets are ever returned.

Every suggestion is validated locally against the original passage before it reaches the response, and ranked by style fit (advisory — ranking never hides or drops a candidate).

Requests are bounded: at most 3 suggestions, 1600 output tokens, and exactly one retry on a transient 5xx. Auth failures, rate limits and malformed responses are never retried, and there is never a second "repair" call.

POST /api/suggestions/validate

Validate one (possibly edited) replacement for a passage. Fully local and deterministic — no provider is involved, so this works even with no provider configured.

This is the endpoint the UI calls immediately before Apply, for both unedited suggestions and text the user changed via "Edit first".

Request

{
  "text": "...",
  "replacement": "...",
  "mode": "selection",
  "selection": { "start": 10, "end": 48, "selected_text": "..." },
  "protected_terms": ["Acme Corp"]
}

Response — SuggestionValidationResponse:

{
  "valid": false,
  "issue_id": null,
  "target_start": 10,
  "target_end": 48,
  "protected_items": [],
  "protected_count": 0,
  "violations": [],
  "semantic_validation": { "status": "PASS", "notes": [] }
}

valid is authoritative. A replacement that changed protected content is never reported as valid. semantic_validation is independent and advisory: PASS / REVIEW / HIGH_RISK / UNAVAILABLE, where UNAVAILABLE is reported honestly when the embedding backend is missing rather than defaulting to PASS.

Request size limits

Limit Value
Document text 200,000 characters
Profile samples 2–12
Characters per sample 60,000
Characters across all samples 200,000
Sample label 120 characters
Suggestions per request 3