generated from coulomb/repo-seed
confidence labels
This commit is contained in:
@@ -82,6 +82,7 @@ curl http://127.0.0.1:8000/repos/1/analysis-runs/1/candidate-graph
|
|||||||
```
|
```
|
||||||
|
|
||||||
Candidate entries are source-linked review seeds. They are not canonical registry truth until a review workflow approves them.
|
Candidate entries are source-linked review seeds. They are not canonical registry truth until a review workflow approves them.
|
||||||
|
Candidate, approved, and search responses include numeric confidence values plus `low`, `medium`, or `high` confidence labels for quick triage.
|
||||||
|
|
||||||
Approve a candidate graph into the canonical registry:
|
Approve a candidate graph into the canonical registry:
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,14 @@ from dataclasses import dataclass, field
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
|
def confidence_label(confidence: float) -> str:
|
||||||
|
if confidence >= 0.8:
|
||||||
|
return "high"
|
||||||
|
if confidence >= 0.5:
|
||||||
|
return "medium"
|
||||||
|
return "low"
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class Repository:
|
class Repository:
|
||||||
id: int
|
id: int
|
||||||
@@ -107,6 +115,7 @@ class CandidateFeature:
|
|||||||
confidence: float
|
confidence: float
|
||||||
status: str
|
status: str
|
||||||
source_refs: list[SourceReference]
|
source_refs: list[SourceReference]
|
||||||
|
confidence_label: str = ""
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
@@ -119,6 +128,7 @@ class CandidateCapability:
|
|||||||
confidence: float
|
confidence: float
|
||||||
status: str
|
status: str
|
||||||
source_refs: list[SourceReference]
|
source_refs: list[SourceReference]
|
||||||
|
confidence_label: str = ""
|
||||||
features: list[CandidateFeature] = field(default_factory=list)
|
features: list[CandidateFeature] = field(default_factory=list)
|
||||||
evidence: list[CandidateEvidence] = field(default_factory=list)
|
evidence: list[CandidateEvidence] = field(default_factory=list)
|
||||||
|
|
||||||
@@ -131,6 +141,7 @@ class CandidateAbility:
|
|||||||
confidence: float
|
confidence: float
|
||||||
status: str
|
status: str
|
||||||
source_refs: list[SourceReference]
|
source_refs: list[SourceReference]
|
||||||
|
confidence_label: str = ""
|
||||||
capabilities: list[CandidateCapability] = field(default_factory=list)
|
capabilities: list[CandidateCapability] = field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
@@ -157,6 +168,7 @@ class Feature:
|
|||||||
type: str
|
type: str
|
||||||
location: str
|
location: str
|
||||||
confidence: float
|
confidence: float
|
||||||
|
confidence_label: str = ""
|
||||||
source_refs: list[SourceReference] = field(default_factory=list)
|
source_refs: list[SourceReference] = field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
@@ -168,6 +180,7 @@ class Capability:
|
|||||||
inputs: list[str]
|
inputs: list[str]
|
||||||
outputs: list[str]
|
outputs: list[str]
|
||||||
confidence: float
|
confidence: float
|
||||||
|
confidence_label: str = ""
|
||||||
features: list[Feature] = field(default_factory=list)
|
features: list[Feature] = field(default_factory=list)
|
||||||
evidence: list[Evidence] = field(default_factory=list)
|
evidence: list[Evidence] = field(default_factory=list)
|
||||||
|
|
||||||
@@ -178,6 +191,7 @@ class Ability:
|
|||||||
name: str
|
name: str
|
||||||
description: str
|
description: str
|
||||||
confidence: float
|
confidence: float
|
||||||
|
confidence_label: str = ""
|
||||||
capabilities: list[Capability] = field(default_factory=list)
|
capabilities: list[Capability] = field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
@@ -194,6 +208,7 @@ class SearchResult:
|
|||||||
match_type: str
|
match_type: str
|
||||||
match_name: str
|
match_name: str
|
||||||
confidence: float
|
confidence: float
|
||||||
|
confidence_label: str = ""
|
||||||
match_description: str = ""
|
match_description: str = ""
|
||||||
matched_field: str = ""
|
matched_field: str = ""
|
||||||
ability_id: int | None = None
|
ability_id: int | None = None
|
||||||
@@ -212,6 +227,7 @@ class AbilitySummary:
|
|||||||
name: str
|
name: str
|
||||||
description: str
|
description: str
|
||||||
confidence: float
|
confidence: float
|
||||||
|
confidence_label: str = ""
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
@@ -224,3 +240,4 @@ class CapabilitySummary:
|
|||||||
name: str
|
name: str
|
||||||
description: str
|
description: str
|
||||||
confidence: float
|
confidence: float
|
||||||
|
confidence_label: str = ""
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ from repo_registry.core.models import (
|
|||||||
ReviewDecision,
|
ReviewDecision,
|
||||||
SearchResult,
|
SearchResult,
|
||||||
SourceReference,
|
SourceReference,
|
||||||
|
confidence_label,
|
||||||
)
|
)
|
||||||
from repo_registry.content_indexing.extractor import ContentChunkCandidate
|
from repo_registry.content_indexing.extractor import ContentChunkCandidate
|
||||||
from repo_registry.candidate_graph.generator import CandidateAbilityDraft
|
from repo_registry.candidate_graph.generator import CandidateAbilityDraft
|
||||||
@@ -401,6 +402,7 @@ class RegistryStore:
|
|||||||
confidence=row["confidence"],
|
confidence=row["confidence"],
|
||||||
status=row["status"],
|
status=row["status"],
|
||||||
source_refs=self._source_refs_from_json(row["source_refs"]),
|
source_refs=self._source_refs_from_json(row["source_refs"]),
|
||||||
|
confidence_label=confidence_label(row["confidence"]),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -429,6 +431,7 @@ class RegistryStore:
|
|||||||
confidence=row["confidence"],
|
confidence=row["confidence"],
|
||||||
status=row["status"],
|
status=row["status"],
|
||||||
source_refs=self._source_refs_from_json(row["source_refs"]),
|
source_refs=self._source_refs_from_json(row["source_refs"]),
|
||||||
|
confidence_label=confidence_label(row["confidence"]),
|
||||||
features=features_by_capability.get(row["id"], []),
|
features=features_by_capability.get(row["id"], []),
|
||||||
evidence=evidence_by_capability.get(row["id"], []),
|
evidence=evidence_by_capability.get(row["id"], []),
|
||||||
)
|
)
|
||||||
@@ -442,6 +445,7 @@ class RegistryStore:
|
|||||||
confidence=row["confidence"],
|
confidence=row["confidence"],
|
||||||
status=row["status"],
|
status=row["status"],
|
||||||
source_refs=self._source_refs_from_json(row["source_refs"]),
|
source_refs=self._source_refs_from_json(row["source_refs"]),
|
||||||
|
confidence_label=confidence_label(row["confidence"]),
|
||||||
capabilities=capabilities_by_ability.get(row["id"], []),
|
capabilities=capabilities_by_ability.get(row["id"], []),
|
||||||
)
|
)
|
||||||
for row in ability_rows
|
for row in ability_rows
|
||||||
@@ -1119,6 +1123,7 @@ class RegistryStore:
|
|||||||
name=row["name"],
|
name=row["name"],
|
||||||
description=row["description"],
|
description=row["description"],
|
||||||
confidence=row["confidence"],
|
confidence=row["confidence"],
|
||||||
|
confidence_label=confidence_label(row["confidence"]),
|
||||||
)
|
)
|
||||||
for row in rows
|
for row in rows
|
||||||
]
|
]
|
||||||
@@ -1146,6 +1151,7 @@ class RegistryStore:
|
|||||||
name=row["name"],
|
name=row["name"],
|
||||||
description=row["description"],
|
description=row["description"],
|
||||||
confidence=row["confidence"],
|
confidence=row["confidence"],
|
||||||
|
confidence_label=confidence_label(row["confidence"]),
|
||||||
)
|
)
|
||||||
for row in rows
|
for row in rows
|
||||||
]
|
]
|
||||||
@@ -1555,6 +1561,7 @@ class RegistryStore:
|
|||||||
type=row["type"],
|
type=row["type"],
|
||||||
location=row["location"],
|
location=row["location"],
|
||||||
confidence=row["confidence"],
|
confidence=row["confidence"],
|
||||||
|
confidence_label=confidence_label(row["confidence"]),
|
||||||
source_refs=self._source_refs_from_json(row["source_refs"]),
|
source_refs=self._source_refs_from_json(row["source_refs"]),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -1581,6 +1588,7 @@ class RegistryStore:
|
|||||||
inputs=json.loads(row["inputs"]),
|
inputs=json.loads(row["inputs"]),
|
||||||
outputs=json.loads(row["outputs"]),
|
outputs=json.loads(row["outputs"]),
|
||||||
confidence=row["confidence"],
|
confidence=row["confidence"],
|
||||||
|
confidence_label=confidence_label(row["confidence"]),
|
||||||
features=features_by_capability.get(row["id"], []),
|
features=features_by_capability.get(row["id"], []),
|
||||||
evidence=evidence_by_capability.get(row["id"], []),
|
evidence=evidence_by_capability.get(row["id"], []),
|
||||||
)
|
)
|
||||||
@@ -1592,6 +1600,7 @@ class RegistryStore:
|
|||||||
name=row["name"],
|
name=row["name"],
|
||||||
description=row["description"],
|
description=row["description"],
|
||||||
confidence=row["confidence"],
|
confidence=row["confidence"],
|
||||||
|
confidence_label=confidence_label(row["confidence"]),
|
||||||
capabilities=capabilities_by_ability.get(row["id"], []),
|
capabilities=capabilities_by_ability.get(row["id"], []),
|
||||||
)
|
)
|
||||||
for row in ability_rows
|
for row in ability_rows
|
||||||
@@ -1706,9 +1715,10 @@ class RegistryStore:
|
|||||||
repository_name=row["repository_name"],
|
repository_name=row["repository_name"],
|
||||||
match_type="repository",
|
match_type="repository",
|
||||||
match_name=row["repository_name"],
|
match_name=row["repository_name"],
|
||||||
|
confidence=1.0,
|
||||||
|
confidence_label=confidence_label(1.0),
|
||||||
match_description=row["description"] or "",
|
match_description=row["description"] or "",
|
||||||
matched_field=matched_field,
|
matched_field=matched_field,
|
||||||
confidence=1.0,
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
for row in ability_rows:
|
for row in ability_rows:
|
||||||
@@ -1721,9 +1731,10 @@ class RegistryStore:
|
|||||||
repository_name=row["repository_name"],
|
repository_name=row["repository_name"],
|
||||||
match_type="ability",
|
match_type="ability",
|
||||||
match_name=row["ability_name"],
|
match_name=row["ability_name"],
|
||||||
|
confidence=row["confidence"],
|
||||||
|
confidence_label=confidence_label(row["confidence"]),
|
||||||
match_description=row["ability_description"],
|
match_description=row["ability_description"],
|
||||||
matched_field=matched_field,
|
matched_field=matched_field,
|
||||||
confidence=row["confidence"],
|
|
||||||
ability_id=row["ability_id"],
|
ability_id=row["ability_id"],
|
||||||
ability_name=row["ability_name"],
|
ability_name=row["ability_name"],
|
||||||
)
|
)
|
||||||
@@ -1740,9 +1751,10 @@ class RegistryStore:
|
|||||||
repository_name=row["repository_name"],
|
repository_name=row["repository_name"],
|
||||||
match_type="capability",
|
match_type="capability",
|
||||||
match_name=row["capability_name"],
|
match_name=row["capability_name"],
|
||||||
|
confidence=row["confidence"],
|
||||||
|
confidence_label=confidence_label(row["confidence"]),
|
||||||
match_description=row["capability_description"],
|
match_description=row["capability_description"],
|
||||||
matched_field=matched_field,
|
matched_field=matched_field,
|
||||||
confidence=row["confidence"],
|
|
||||||
ability_id=row["ability_id"],
|
ability_id=row["ability_id"],
|
||||||
ability_name=row["ability_name"],
|
ability_name=row["ability_name"],
|
||||||
capability_id=row["capability_id"],
|
capability_id=row["capability_id"],
|
||||||
@@ -1764,9 +1776,10 @@ class RegistryStore:
|
|||||||
repository_name=row["repository_name"],
|
repository_name=row["repository_name"],
|
||||||
match_type="feature",
|
match_type="feature",
|
||||||
match_name=row["feature_name"],
|
match_name=row["feature_name"],
|
||||||
|
confidence=row["confidence"],
|
||||||
|
confidence_label=confidence_label(row["confidence"]),
|
||||||
match_description=row["feature_type"],
|
match_description=row["feature_type"],
|
||||||
matched_field=matched_field,
|
matched_field=matched_field,
|
||||||
confidence=row["confidence"],
|
|
||||||
ability_id=row["ability_id"],
|
ability_id=row["ability_id"],
|
||||||
ability_name=row["ability_name"],
|
ability_name=row["ability_name"],
|
||||||
capability_id=row["capability_id"],
|
capability_id=row["capability_id"],
|
||||||
@@ -1789,9 +1802,12 @@ class RegistryStore:
|
|||||||
repository_name=row["repository_name"],
|
repository_name=row["repository_name"],
|
||||||
match_type="evidence",
|
match_type="evidence",
|
||||||
match_name=row["reference"],
|
match_name=row["reference"],
|
||||||
|
confidence=self._evidence_confidence(row["strength"]),
|
||||||
|
confidence_label=confidence_label(
|
||||||
|
self._evidence_confidence(row["strength"])
|
||||||
|
),
|
||||||
match_description=row["evidence_type"],
|
match_description=row["evidence_type"],
|
||||||
matched_field=matched_field,
|
matched_field=matched_field,
|
||||||
confidence=self._evidence_confidence(row["strength"]),
|
|
||||||
ability_id=row["ability_id"],
|
ability_id=row["ability_id"],
|
||||||
ability_name=row["ability_name"],
|
ability_name=row["ability_name"],
|
||||||
capability_id=row["capability_id"],
|
capability_id=row["capability_id"],
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|||||||
from dataclasses import asdict
|
from dataclasses import asdict
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from fastapi import Depends, FastAPI, HTTPException
|
from fastapi import Depends, FastAPI, HTTPException, Query
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||||
|
|
||||||
@@ -324,7 +324,26 @@ class CandidateEvidenceMerge(BaseModel):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
app = FastAPI(title="Repository Ability Registry", version="0.1.0")
|
API_DESCRIPTION = (
|
||||||
|
"Register repositories, analyze their observable implementation facts, "
|
||||||
|
"curate reviewable ability graphs, and search approved repository abilities."
|
||||||
|
)
|
||||||
|
|
||||||
|
OPENAPI_TAGS = [
|
||||||
|
{"name": "health", "description": "Service health checks."},
|
||||||
|
{"name": "repositories", "description": "Repository registration and metadata."},
|
||||||
|
{"name": "analysis", "description": "Repository scans and extracted review inputs."},
|
||||||
|
{"name": "review", "description": "Candidate graph approval and correction workflow."},
|
||||||
|
{"name": "registry", "description": "Approved ability maps and manual registry CRUD."},
|
||||||
|
{"name": "search", "description": "Agent-facing discovery endpoints."},
|
||||||
|
]
|
||||||
|
|
||||||
|
app = FastAPI(
|
||||||
|
title="Repository Ability Registry",
|
||||||
|
version="0.1.0",
|
||||||
|
description=API_DESCRIPTION,
|
||||||
|
openapi_tags=OPENAPI_TAGS,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
from repo_registry.web_ui.views import router as ui_router
|
from repo_registry.web_ui.views import router as ui_router
|
||||||
@@ -332,12 +351,12 @@ from repo_registry.web_ui.views import router as ui_router
|
|||||||
app.include_router(ui_router)
|
app.include_router(ui_router)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/health")
|
@app.get("/health", tags=["health"])
|
||||||
def health() -> dict[str, str]:
|
def health() -> dict[str, str]:
|
||||||
return {"status": "ok"}
|
return {"status": "ok"}
|
||||||
|
|
||||||
|
|
||||||
@app.post("/repos", status_code=201)
|
@app.post("/repos", status_code=201, tags=["repositories"])
|
||||||
def create_repository(
|
def create_repository(
|
||||||
payload: RepositoryCreate,
|
payload: RepositoryCreate,
|
||||||
service: RegistryService = Depends(get_service),
|
service: RegistryService = Depends(get_service),
|
||||||
@@ -349,14 +368,14 @@ def create_repository(
|
|||||||
return asdict(repository)
|
return asdict(repository)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/repos")
|
@app.get("/repos", tags=["repositories"])
|
||||||
def list_repositories(
|
def list_repositories(
|
||||||
service: RegistryService = Depends(get_service),
|
service: RegistryService = Depends(get_service),
|
||||||
) -> list[dict[str, object]]:
|
) -> list[dict[str, object]]:
|
||||||
return [asdict(repository) for repository in service.list_repositories()]
|
return [asdict(repository) for repository in service.list_repositories()]
|
||||||
|
|
||||||
|
|
||||||
@app.get("/repos/{repository_id}")
|
@app.get("/repos/{repository_id}", tags=["repositories"])
|
||||||
def get_repository(
|
def get_repository(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
service: RegistryService = Depends(get_service),
|
service: RegistryService = Depends(get_service),
|
||||||
@@ -367,7 +386,7 @@ def get_repository(
|
|||||||
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||||||
|
|
||||||
|
|
||||||
@app.patch("/repos/{repository_id}")
|
@app.patch("/repos/{repository_id}", tags=["repositories"])
|
||||||
def update_repository(
|
def update_repository(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
payload: RepositoryUpdate,
|
payload: RepositoryUpdate,
|
||||||
@@ -384,7 +403,7 @@ def update_repository(
|
|||||||
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||||||
|
|
||||||
|
|
||||||
@app.delete("/repos/{repository_id}", status_code=204)
|
@app.delete("/repos/{repository_id}", status_code=204, tags=["repositories"])
|
||||||
def delete_repository(
|
def delete_repository(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
service: RegistryService = Depends(get_service),
|
service: RegistryService = Depends(get_service),
|
||||||
@@ -395,7 +414,7 @@ def delete_repository(
|
|||||||
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||||||
|
|
||||||
|
|
||||||
@app.post("/repos/{repository_id}/analysis-runs", status_code=201)
|
@app.post("/repos/{repository_id}/analysis-runs", status_code=201, tags=["analysis"])
|
||||||
def create_analysis_run(
|
def create_analysis_run(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
payload: AnalysisRunCreate,
|
payload: AnalysisRunCreate,
|
||||||
@@ -411,7 +430,7 @@ def create_analysis_run(
|
|||||||
return asdict(summary)
|
return asdict(summary)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/repos/{repository_id}/analysis-runs")
|
@app.get("/repos/{repository_id}/analysis-runs", tags=["analysis"])
|
||||||
def list_analysis_runs(
|
def list_analysis_runs(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
service: RegistryService = Depends(get_service),
|
service: RegistryService = Depends(get_service),
|
||||||
@@ -422,7 +441,7 @@ def list_analysis_runs(
|
|||||||
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||||||
|
|
||||||
|
|
||||||
@app.get("/repos/{repository_id}/analysis-runs/{analysis_run_id}")
|
@app.get("/repos/{repository_id}/analysis-runs/{analysis_run_id}", tags=["analysis"])
|
||||||
def get_analysis_run(
|
def get_analysis_run(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
analysis_run_id: int,
|
analysis_run_id: int,
|
||||||
@@ -434,7 +453,7 @@ def get_analysis_run(
|
|||||||
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||||||
|
|
||||||
|
|
||||||
@app.get("/repos/{repository_id}/review-decisions")
|
@app.get("/repos/{repository_id}/review-decisions", tags=["review"])
|
||||||
def list_repository_review_decisions(
|
def list_repository_review_decisions(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
service: RegistryService = Depends(get_service),
|
service: RegistryService = Depends(get_service),
|
||||||
@@ -448,7 +467,10 @@ def list_repository_review_decisions(
|
|||||||
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||||||
|
|
||||||
|
|
||||||
@app.get("/repos/{repository_id}/analysis-runs/{analysis_run_id}/review-decisions")
|
@app.get(
|
||||||
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/review-decisions",
|
||||||
|
tags=["review"],
|
||||||
|
)
|
||||||
def list_analysis_run_review_decisions(
|
def list_analysis_run_review_decisions(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
analysis_run_id: int,
|
analysis_run_id: int,
|
||||||
@@ -466,7 +488,7 @@ def list_analysis_run_review_decisions(
|
|||||||
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||||||
|
|
||||||
|
|
||||||
@app.get("/repos/{repository_id}/observed-facts")
|
@app.get("/repos/{repository_id}/observed-facts", tags=["analysis"])
|
||||||
def list_observed_facts(
|
def list_observed_facts(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
analysis_run_id: int | None = None,
|
analysis_run_id: int | None = None,
|
||||||
@@ -481,7 +503,7 @@ def list_observed_facts(
|
|||||||
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||||||
|
|
||||||
|
|
||||||
@app.get("/repos/{repository_id}/content-chunks")
|
@app.get("/repos/{repository_id}/content-chunks", tags=["analysis"])
|
||||||
def list_content_chunks(
|
def list_content_chunks(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
analysis_run_id: int | None = None,
|
analysis_run_id: int | None = None,
|
||||||
@@ -496,7 +518,10 @@ def list_content_chunks(
|
|||||||
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||||||
|
|
||||||
|
|
||||||
@app.get("/repos/{repository_id}/analysis-runs/{analysis_run_id}/content-chunks")
|
@app.get(
|
||||||
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/content-chunks",
|
||||||
|
tags=["analysis"],
|
||||||
|
)
|
||||||
def list_analysis_run_content_chunks(
|
def list_analysis_run_content_chunks(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
analysis_run_id: int,
|
analysis_run_id: int,
|
||||||
@@ -511,7 +536,10 @@ def list_analysis_run_content_chunks(
|
|||||||
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||||||
|
|
||||||
|
|
||||||
@app.get("/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-graph")
|
@app.get(
|
||||||
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-graph",
|
||||||
|
tags=["review"],
|
||||||
|
)
|
||||||
def get_candidate_graph(
|
def get_candidate_graph(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
analysis_run_id: int,
|
analysis_run_id: int,
|
||||||
@@ -523,7 +551,10 @@ def get_candidate_graph(
|
|||||||
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||||||
|
|
||||||
|
|
||||||
@app.post("/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-graph/approve")
|
@app.post(
|
||||||
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-graph/approve",
|
||||||
|
tags=["review"],
|
||||||
|
)
|
||||||
def approve_candidate_graph(
|
def approve_candidate_graph(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
analysis_run_id: int,
|
analysis_run_id: int,
|
||||||
@@ -544,7 +575,8 @@ def approve_candidate_graph(
|
|||||||
|
|
||||||
@app.post(
|
@app.post(
|
||||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}"
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}"
|
||||||
"/candidate-abilities/{candidate_ability_id}/reject"
|
"/candidate-abilities/{candidate_ability_id}/reject",
|
||||||
|
tags=["review"],
|
||||||
)
|
)
|
||||||
def reject_candidate_ability(
|
def reject_candidate_ability(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
@@ -568,7 +600,8 @@ def reject_candidate_ability(
|
|||||||
|
|
||||||
@app.post(
|
@app.post(
|
||||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}"
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}"
|
||||||
"/candidate-capabilities/{candidate_capability_id}/reject"
|
"/candidate-capabilities/{candidate_capability_id}/reject",
|
||||||
|
tags=["review"],
|
||||||
)
|
)
|
||||||
def reject_candidate_capability(
|
def reject_candidate_capability(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
@@ -592,7 +625,8 @@ def reject_candidate_capability(
|
|||||||
|
|
||||||
@app.post(
|
@app.post(
|
||||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}"
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}"
|
||||||
"/candidate-features/{candidate_feature_id}/reject"
|
"/candidate-features/{candidate_feature_id}/reject",
|
||||||
|
tags=["review"],
|
||||||
)
|
)
|
||||||
def reject_candidate_feature(
|
def reject_candidate_feature(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
@@ -616,7 +650,8 @@ def reject_candidate_feature(
|
|||||||
|
|
||||||
@app.post(
|
@app.post(
|
||||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}"
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}"
|
||||||
"/candidate-evidence/{candidate_evidence_id}/reject"
|
"/candidate-evidence/{candidate_evidence_id}/reject",
|
||||||
|
tags=["review"],
|
||||||
)
|
)
|
||||||
def reject_candidate_evidence(
|
def reject_candidate_evidence(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
@@ -640,7 +675,8 @@ def reject_candidate_evidence(
|
|||||||
|
|
||||||
@app.patch(
|
@app.patch(
|
||||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}"
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}"
|
||||||
"/candidate-abilities/{candidate_ability_id}"
|
"/candidate-abilities/{candidate_ability_id}",
|
||||||
|
tags=["review"],
|
||||||
)
|
)
|
||||||
def edit_candidate_ability(
|
def edit_candidate_ability(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
@@ -664,7 +700,8 @@ def edit_candidate_ability(
|
|||||||
|
|
||||||
@app.patch(
|
@app.patch(
|
||||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}"
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}"
|
||||||
"/candidate-capabilities/{candidate_capability_id}"
|
"/candidate-capabilities/{candidate_capability_id}",
|
||||||
|
tags=["review"],
|
||||||
)
|
)
|
||||||
def edit_candidate_capability(
|
def edit_candidate_capability(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
@@ -688,7 +725,8 @@ def edit_candidate_capability(
|
|||||||
|
|
||||||
@app.post(
|
@app.post(
|
||||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}"
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}"
|
||||||
"/candidate-capabilities/{candidate_capability_id}/relink"
|
"/candidate-capabilities/{candidate_capability_id}/relink",
|
||||||
|
tags=["review"],
|
||||||
)
|
)
|
||||||
def relink_candidate_capability(
|
def relink_candidate_capability(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
@@ -712,7 +750,8 @@ def relink_candidate_capability(
|
|||||||
|
|
||||||
@app.post(
|
@app.post(
|
||||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}"
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}"
|
||||||
"/candidate-features/{candidate_feature_id}/relink"
|
"/candidate-features/{candidate_feature_id}/relink",
|
||||||
|
tags=["review"],
|
||||||
)
|
)
|
||||||
def relink_candidate_feature(
|
def relink_candidate_feature(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
@@ -736,7 +775,8 @@ def relink_candidate_feature(
|
|||||||
|
|
||||||
@app.post(
|
@app.post(
|
||||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}"
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}"
|
||||||
"/candidate-evidence/{candidate_evidence_id}/relink"
|
"/candidate-evidence/{candidate_evidence_id}/relink",
|
||||||
|
tags=["review"],
|
||||||
)
|
)
|
||||||
def relink_candidate_evidence(
|
def relink_candidate_evidence(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
@@ -760,7 +800,8 @@ def relink_candidate_evidence(
|
|||||||
|
|
||||||
@app.post(
|
@app.post(
|
||||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}"
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}"
|
||||||
"/candidate-abilities/{source_ability_id}/merge"
|
"/candidate-abilities/{source_ability_id}/merge",
|
||||||
|
tags=["review"],
|
||||||
)
|
)
|
||||||
def merge_candidate_ability(
|
def merge_candidate_ability(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
@@ -784,7 +825,8 @@ def merge_candidate_ability(
|
|||||||
|
|
||||||
@app.post(
|
@app.post(
|
||||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}"
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}"
|
||||||
"/candidate-capabilities/{source_capability_id}/merge"
|
"/candidate-capabilities/{source_capability_id}/merge",
|
||||||
|
tags=["review"],
|
||||||
)
|
)
|
||||||
def merge_candidate_capability(
|
def merge_candidate_capability(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
@@ -808,7 +850,8 @@ def merge_candidate_capability(
|
|||||||
|
|
||||||
@app.post(
|
@app.post(
|
||||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}"
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}"
|
||||||
"/candidate-features/{source_feature_id}/merge"
|
"/candidate-features/{source_feature_id}/merge",
|
||||||
|
tags=["review"],
|
||||||
)
|
)
|
||||||
def merge_candidate_feature(
|
def merge_candidate_feature(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
@@ -832,7 +875,8 @@ def merge_candidate_feature(
|
|||||||
|
|
||||||
@app.post(
|
@app.post(
|
||||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}"
|
"/repos/{repository_id}/analysis-runs/{analysis_run_id}"
|
||||||
"/candidate-evidence/{source_evidence_id}/merge"
|
"/candidate-evidence/{source_evidence_id}/merge",
|
||||||
|
tags=["review"],
|
||||||
)
|
)
|
||||||
def merge_candidate_evidence(
|
def merge_candidate_evidence(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
@@ -854,7 +898,7 @@ def merge_candidate_evidence(
|
|||||||
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
||||||
|
|
||||||
|
|
||||||
@app.post("/repos/{repository_id}/abilities", status_code=201)
|
@app.post("/repos/{repository_id}/abilities", status_code=201, tags=["registry"])
|
||||||
def create_ability(
|
def create_ability(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
payload: AbilityCreate,
|
payload: AbilityCreate,
|
||||||
@@ -867,7 +911,7 @@ def create_ability(
|
|||||||
return {"id": ability_id}
|
return {"id": ability_id}
|
||||||
|
|
||||||
|
|
||||||
@app.patch("/repos/{repository_id}/abilities/{ability_id}")
|
@app.patch("/repos/{repository_id}/abilities/{ability_id}", tags=["registry"])
|
||||||
def update_ability(
|
def update_ability(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
ability_id: int,
|
ability_id: int,
|
||||||
@@ -886,7 +930,7 @@ def update_ability(
|
|||||||
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||||||
|
|
||||||
|
|
||||||
@app.delete("/repos/{repository_id}/abilities/{ability_id}")
|
@app.delete("/repos/{repository_id}/abilities/{ability_id}", tags=["registry"])
|
||||||
def delete_ability(
|
def delete_ability(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
ability_id: int,
|
ability_id: int,
|
||||||
@@ -898,7 +942,7 @@ def delete_ability(
|
|||||||
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||||||
|
|
||||||
|
|
||||||
@app.post("/repos/{repository_id}/capabilities", status_code=201)
|
@app.post("/repos/{repository_id}/capabilities", status_code=201, tags=["registry"])
|
||||||
def create_capability(
|
def create_capability(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
payload: CapabilityCreate,
|
payload: CapabilityCreate,
|
||||||
@@ -911,7 +955,7 @@ def create_capability(
|
|||||||
return {"id": capability_id}
|
return {"id": capability_id}
|
||||||
|
|
||||||
|
|
||||||
@app.patch("/repos/{repository_id}/capabilities/{capability_id}")
|
@app.patch("/repos/{repository_id}/capabilities/{capability_id}", tags=["registry"])
|
||||||
def update_capability(
|
def update_capability(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
capability_id: int,
|
capability_id: int,
|
||||||
@@ -930,7 +974,7 @@ def update_capability(
|
|||||||
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||||||
|
|
||||||
|
|
||||||
@app.delete("/repos/{repository_id}/capabilities/{capability_id}")
|
@app.delete("/repos/{repository_id}/capabilities/{capability_id}", tags=["registry"])
|
||||||
def delete_capability(
|
def delete_capability(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
capability_id: int,
|
capability_id: int,
|
||||||
@@ -942,7 +986,7 @@ def delete_capability(
|
|||||||
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||||||
|
|
||||||
|
|
||||||
@app.post("/repos/{repository_id}/features", status_code=201)
|
@app.post("/repos/{repository_id}/features", status_code=201, tags=["registry"])
|
||||||
def create_feature(
|
def create_feature(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
payload: FeatureCreate,
|
payload: FeatureCreate,
|
||||||
@@ -955,7 +999,7 @@ def create_feature(
|
|||||||
return {"id": feature_id}
|
return {"id": feature_id}
|
||||||
|
|
||||||
|
|
||||||
@app.patch("/repos/{repository_id}/features/{feature_id}")
|
@app.patch("/repos/{repository_id}/features/{feature_id}", tags=["registry"])
|
||||||
def update_feature(
|
def update_feature(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
feature_id: int,
|
feature_id: int,
|
||||||
@@ -974,7 +1018,7 @@ def update_feature(
|
|||||||
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||||||
|
|
||||||
|
|
||||||
@app.delete("/repos/{repository_id}/features/{feature_id}")
|
@app.delete("/repos/{repository_id}/features/{feature_id}", tags=["registry"])
|
||||||
def delete_feature(
|
def delete_feature(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
feature_id: int,
|
feature_id: int,
|
||||||
@@ -986,7 +1030,7 @@ def delete_feature(
|
|||||||
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||||||
|
|
||||||
|
|
||||||
@app.post("/repos/{repository_id}/evidence", status_code=201)
|
@app.post("/repos/{repository_id}/evidence", status_code=201, tags=["registry"])
|
||||||
def create_evidence(
|
def create_evidence(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
payload: EvidenceCreate,
|
payload: EvidenceCreate,
|
||||||
@@ -999,7 +1043,7 @@ def create_evidence(
|
|||||||
return {"id": evidence_id}
|
return {"id": evidence_id}
|
||||||
|
|
||||||
|
|
||||||
@app.patch("/repos/{repository_id}/evidence/{evidence_id}")
|
@app.patch("/repos/{repository_id}/evidence/{evidence_id}", tags=["registry"])
|
||||||
def update_evidence(
|
def update_evidence(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
evidence_id: int,
|
evidence_id: int,
|
||||||
@@ -1018,7 +1062,7 @@ def update_evidence(
|
|||||||
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||||||
|
|
||||||
|
|
||||||
@app.delete("/repos/{repository_id}/evidence/{evidence_id}")
|
@app.delete("/repos/{repository_id}/evidence/{evidence_id}", tags=["registry"])
|
||||||
def delete_evidence(
|
def delete_evidence(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
evidence_id: int,
|
evidence_id: int,
|
||||||
@@ -1030,7 +1074,7 @@ def delete_evidence(
|
|||||||
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||||||
|
|
||||||
|
|
||||||
@app.get("/repos/{repository_id}/ability-map")
|
@app.get("/repos/{repository_id}/ability-map", tags=["registry"])
|
||||||
def get_ability_map(
|
def get_ability_map(
|
||||||
repository_id: int,
|
repository_id: int,
|
||||||
service: RegistryService = Depends(get_service),
|
service: RegistryService = Depends(get_service),
|
||||||
@@ -1041,14 +1085,33 @@ def get_ability_map(
|
|||||||
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
raise HTTPException(status_code=404, detail=str(exc)) from exc
|
||||||
|
|
||||||
|
|
||||||
@app.get("/search")
|
@app.get("/search", tags=["search"])
|
||||||
def search(
|
def search(
|
||||||
q: str,
|
q: str = Query(
|
||||||
status: str | None = None,
|
...,
|
||||||
language: str | None = None,
|
description="Natural-language or keyword query over approved registry entries.",
|
||||||
framework: str | None = None,
|
examples=["classify email", "FastAPI health endpoint"],
|
||||||
ability: str | None = None,
|
),
|
||||||
capability: str | None = None,
|
status: str | None = Query(
|
||||||
|
default=None,
|
||||||
|
description="Filter repositories by registry status, such as indexed.",
|
||||||
|
),
|
||||||
|
language: str | None = Query(
|
||||||
|
default=None,
|
||||||
|
description="Filter by observed programming language.",
|
||||||
|
),
|
||||||
|
framework: str | None = Query(
|
||||||
|
default=None,
|
||||||
|
description="Filter by observed framework hint.",
|
||||||
|
),
|
||||||
|
ability: str | None = Query(
|
||||||
|
default=None,
|
||||||
|
description="Filter to results under an approved ability name.",
|
||||||
|
),
|
||||||
|
capability: str | None = Query(
|
||||||
|
default=None,
|
||||||
|
description="Filter to results under an approved capability name.",
|
||||||
|
),
|
||||||
service: RegistryService = Depends(get_service),
|
service: RegistryService = Depends(get_service),
|
||||||
) -> list[dict[str, object]]:
|
) -> list[dict[str, object]]:
|
||||||
return [
|
return [
|
||||||
@@ -1064,14 +1127,14 @@ def search(
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@app.get("/abilities")
|
@app.get("/abilities", tags=["search"])
|
||||||
def list_abilities(
|
def list_abilities(
|
||||||
service: RegistryService = Depends(get_service),
|
service: RegistryService = Depends(get_service),
|
||||||
) -> list[dict[str, object]]:
|
) -> list[dict[str, object]]:
|
||||||
return [asdict(ability) for ability in service.list_abilities()]
|
return [asdict(ability) for ability in service.list_abilities()]
|
||||||
|
|
||||||
|
|
||||||
@app.get("/capabilities")
|
@app.get("/capabilities", tags=["search"])
|
||||||
def list_capabilities(
|
def list_capabilities(
|
||||||
service: RegistryService = Depends(get_service),
|
service: RegistryService = Depends(get_service),
|
||||||
) -> list[dict[str, object]]:
|
) -> list[dict[str, object]]:
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ def search_page(
|
|||||||
{render_search_context(asdict(result))}
|
{render_search_context(asdict(result))}
|
||||||
</td>
|
</td>
|
||||||
<td>{escape(result.matched_field)}</td>
|
<td>{escape(result.matched_field)}</td>
|
||||||
<td>{result.confidence:.2f}</td>
|
<td>{result.confidence:.2f} <span class="pill">{escape(result.confidence_label)}</span></td>
|
||||||
</tr>
|
</tr>
|
||||||
"""
|
"""
|
||||||
for result in results
|
for result in results
|
||||||
@@ -1018,7 +1018,7 @@ def render_candidate_graph(graph: dict, repository_id: int, analysis_run_id: int
|
|||||||
<strong>{escape(ability['name'])}</strong>
|
<strong>{escape(ability['name'])}</strong>
|
||||||
<span class="pill">ID {ability['id']}</span>
|
<span class="pill">ID {ability['id']}</span>
|
||||||
<span class="pill">{escape(ability['status'])}</span>
|
<span class="pill">{escape(ability['status'])}</span>
|
||||||
<span class="pill">{ability['confidence']:.2f}</span>
|
<span class="pill">{ability['confidence']:.2f} {escape(ability['confidence_label'])}</span>
|
||||||
{render_candidate_ability_actions(ability, repository_id, analysis_run_id)}
|
{render_candidate_ability_actions(ability, repository_id, analysis_run_id)}
|
||||||
<p class="muted">{escape(ability['description'])}</p>
|
<p class="muted">{escape(ability['description'])}</p>
|
||||||
{render_candidate_edit_form('candidate-abilities', ability, repository_id, analysis_run_id)}
|
{render_candidate_edit_form('candidate-abilities', ability, repository_id, analysis_run_id)}
|
||||||
@@ -1150,7 +1150,7 @@ def render_candidate_capability(
|
|||||||
<strong>{escape(capability['name'])}</strong>
|
<strong>{escape(capability['name'])}</strong>
|
||||||
<span class="pill">ID {capability['id']}</span>
|
<span class="pill">ID {capability['id']}</span>
|
||||||
<span class="pill">{escape(capability['status'])}</span>
|
<span class="pill">{escape(capability['status'])}</span>
|
||||||
<span class="pill">{capability['confidence']:.2f}</span>
|
<span class="pill">{capability['confidence']:.2f} {escape(capability['confidence_label'])}</span>
|
||||||
{render_candidate_reject_form('candidate-capabilities', capability, repository_id, analysis_run_id)}
|
{render_candidate_reject_form('candidate-capabilities', capability, repository_id, analysis_run_id)}
|
||||||
<p class="muted">{escape(capability['description'])}</p>
|
<p class="muted">{escape(capability['description'])}</p>
|
||||||
{render_candidate_edit_form('candidate-capabilities', capability, repository_id, analysis_run_id)}
|
{render_candidate_edit_form('candidate-capabilities', capability, repository_id, analysis_run_id)}
|
||||||
@@ -1287,6 +1287,7 @@ def render_ability_map(ability_map: dict, repository_id: int) -> str:
|
|||||||
<li id="capability-{capability['id']}">
|
<li id="capability-{capability['id']}">
|
||||||
<strong>{escape(capability['name'])}</strong>
|
<strong>{escape(capability['name'])}</strong>
|
||||||
<span class="pill">ID {capability['id']}</span>
|
<span class="pill">ID {capability['id']}</span>
|
||||||
|
<span class="pill">{capability['confidence']:.2f} {escape(capability['confidence_label'])}</span>
|
||||||
<p class="muted">{escape(capability['description'])}</p>
|
<p class="muted">{escape(capability['description'])}</p>
|
||||||
{render_approved_capability_forms(capability, repository_id)}
|
{render_approved_capability_forms(capability, repository_id)}
|
||||||
<ul>{features}{evidence}</ul>
|
<ul>{features}{evidence}</ul>
|
||||||
@@ -1298,6 +1299,7 @@ def render_ability_map(ability_map: dict, repository_id: int) -> str:
|
|||||||
<li id="ability-{ability['id']}">
|
<li id="ability-{ability['id']}">
|
||||||
<strong>{escape(ability['name'])}</strong>
|
<strong>{escape(ability['name'])}</strong>
|
||||||
<span class="pill">ID {ability['id']}</span>
|
<span class="pill">ID {ability['id']}</span>
|
||||||
|
<span class="pill">{ability['confidence']:.2f} {escape(ability['confidence_label'])}</span>
|
||||||
<p class="muted">{escape(ability['description'])}</p>
|
<p class="muted">{escape(ability['description'])}</p>
|
||||||
{render_approved_ability_forms(ability, repository_id)}
|
{render_approved_ability_forms(ability, repository_id)}
|
||||||
<ul>{''.join(capabilities)}</ul>
|
<ul>{''.join(capabilities)}</ul>
|
||||||
@@ -1348,6 +1350,7 @@ def render_approved_feature(feature: dict, repository_id: int) -> str:
|
|||||||
<li>
|
<li>
|
||||||
{escape(feature["name"])}
|
{escape(feature["name"])}
|
||||||
<span class="pill">{escape(feature["type"])}</span>
|
<span class="pill">{escape(feature["type"])}</span>
|
||||||
|
<span class="pill">{feature["confidence"]:.2f} {escape(feature["confidence_label"])}</span>
|
||||||
<span class="source">{escape(feature["location"])}</span>
|
<span class="source">{escape(feature["location"])}</span>
|
||||||
{render_sources(feature.get("source_refs", []))}
|
{render_sources(feature.get("source_refs", []))}
|
||||||
<form class="stack" method="post" action="/ui/repos/{repository_id}/features/{feature['id']}/edit">
|
<form class="stack" method="post" action="/ui/repos/{repository_id}/features/{feature['id']}/edit">
|
||||||
|
|||||||
@@ -4,6 +4,28 @@ from repo_registry.web_api import app as app_module
|
|||||||
from repo_registry.web_api.app import Settings, app, get_service, get_settings
|
from repo_registry.web_api.app import Settings, app, get_service, get_settings
|
||||||
|
|
||||||
|
|
||||||
|
def test_openapi_groups_agent_facing_endpoints():
|
||||||
|
client = TestClient(app)
|
||||||
|
|
||||||
|
response = client.get("/openapi.json")
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
schema = response.json()
|
||||||
|
assert {tag["name"] for tag in schema["tags"]} >= {
|
||||||
|
"repositories",
|
||||||
|
"analysis",
|
||||||
|
"review",
|
||||||
|
"registry",
|
||||||
|
"search",
|
||||||
|
}
|
||||||
|
search_operation = schema["paths"]["/search"]["get"]
|
||||||
|
assert search_operation["tags"] == ["search"]
|
||||||
|
assert {
|
||||||
|
parameter["name"]: parameter["description"]
|
||||||
|
for parameter in search_operation["parameters"]
|
||||||
|
}["q"].startswith("Natural-language")
|
||||||
|
|
||||||
|
|
||||||
def test_api_manual_registry_loop(tmp_path):
|
def test_api_manual_registry_loop(tmp_path):
|
||||||
def override_settings():
|
def override_settings():
|
||||||
return Settings(
|
return Settings(
|
||||||
@@ -323,6 +345,11 @@ def test_api_analysis_run_loop(tmp_path):
|
|||||||
ability_map = approve_response.json()
|
ability_map = approve_response.json()
|
||||||
assert ability_map["repository"]["status"] == "indexed"
|
assert ability_map["repository"]["status"] == "indexed"
|
||||||
assert ability_map["abilities"][0]["name"] == "Frontend Delivery"
|
assert ability_map["abilities"][0]["name"] == "Frontend Delivery"
|
||||||
|
assert ability_map["abilities"][0]["confidence_label"] in {
|
||||||
|
"low",
|
||||||
|
"medium",
|
||||||
|
"high",
|
||||||
|
}
|
||||||
assert ability_map["abilities"][0]["capabilities"][0]["name"] == (
|
assert ability_map["abilities"][0]["capabilities"][0]["name"] == (
|
||||||
"Describe Frontend Stack"
|
"Describe Frontend Stack"
|
||||||
)
|
)
|
||||||
@@ -331,6 +358,7 @@ def test_api_analysis_run_loop(tmp_path):
|
|||||||
assert search_response.status_code == 200
|
assert search_response.status_code == 200
|
||||||
assert search_response.json()
|
assert search_response.json()
|
||||||
assert "matched_field" in search_response.json()[0]
|
assert "matched_field" in search_response.json()[0]
|
||||||
|
assert "confidence_label" in search_response.json()[0]
|
||||||
|
|
||||||
filtered_search_response = client.get(
|
filtered_search_response = client.get(
|
||||||
"/search",
|
"/search",
|
||||||
@@ -348,6 +376,11 @@ def test_api_analysis_run_loop(tmp_path):
|
|||||||
assert abilities_response.status_code == 200
|
assert abilities_response.status_code == 200
|
||||||
assert abilities_response.json()[0]["name"] == "Frontend Delivery"
|
assert abilities_response.json()[0]["name"] == "Frontend Delivery"
|
||||||
assert abilities_response.json()[0]["repository_name"] == "Frontend"
|
assert abilities_response.json()[0]["repository_name"] == "Frontend"
|
||||||
|
assert abilities_response.json()[0]["confidence_label"] in {
|
||||||
|
"low",
|
||||||
|
"medium",
|
||||||
|
"high",
|
||||||
|
}
|
||||||
|
|
||||||
capabilities_response = client.get("/capabilities")
|
capabilities_response = client.get("/capabilities")
|
||||||
assert capabilities_response.status_code == 200
|
assert capabilities_response.status_code == 200
|
||||||
|
|||||||
Reference in New Issue
Block a user