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
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,11 @@ frontend/node_modules/
frontend/dist/
*.egg-info/
.DS_Store

data/taxonomy/imports/*.xlsx
data/taxonomy/exports/*.xlsx
data/reports/*.xlsx
data/reports/*.html
data/reports/*.pdf
*.xls
*.xlsx
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Argument-Risk-Engine is a practical, local, Chrome-first web application for tax
- **Conservative:** uncertain findings are marked as low confidence or omitted.
- **Local-first:** the MVP runs without authentication or a database.
- **Configurable models:** deterministic local analysis is the default; paid LLM providers can be configured through `data/config/model_profiles.yaml`.
- **Workbook friendly:** taxonomy packs can be imported from and exported to Excel workbooks.
- **Workbook friendly:** taxonomy packs can be imported from and exported to Excel workbooks. The real taxonomy workbook is a user-managed external file and is intentionally not committed to Git.

## One-command setup

Expand Down Expand Up @@ -39,6 +39,16 @@ make import-taxonomy # import an Excel taxonomy workbook
make export-taxonomy # export the active taxonomy to Excel
```

## Taxonomy workbook imports

The real taxonomy workbook should be imported later from the dashboard or CLI and is intentionally not committed to Git. Place a local copy under `data/taxonomy/imports/` or choose it from Chrome in the Taxonomy Workbench. For CLI imports, run:

```bash
python scripts/import_taxonomy_excel.py --input data/taxonomy/imports/argument_risk_taxonomy_living_workbook_v2_taxonomy_first.xlsx
```

Generated Excel exports and report files are local artifacts and are ignored by Git. Empty import/export/report directories are kept with `.gitkeep` files.

## API overview

- `POST /api/analysis/analyze` analyzes text and returns claims, risks, evidence, and a conservative summary.
Expand Down
Binary file not shown.
20 changes: 18 additions & 2 deletions backend/app/api/routes_taxonomy_workbench.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,30 @@
from pathlib import Path
from tempfile import NamedTemporaryFile

from backend.app.services.taxonomy_workbench_service import export_workbook, quality
from fastapi import APIRouter
from backend.app.services.taxonomy_workbench_service import export_workbook, import_workbook, quality
from fastapi import APIRouter, File, UploadFile

router = APIRouter(prefix="/taxonomy-workbench", tags=["taxonomy-workbench"])


@router.get("/quality")
def taxonomy_quality() -> dict[str, object]:
return quality()


@router.post("/import")
def import_taxonomy(file: UploadFile = File(...)) -> dict[str, object]:
suffix = Path(getattr(file, "filename", "taxonomy.xlsx") or "taxonomy.xlsx").suffix or ".xlsx"
with NamedTemporaryFile(delete=False, suffix=suffix) as handle:
content = file.file.read()
handle.write(content)
temp_path = Path(handle.name)
try:
return import_workbook(temp_path)
finally:
temp_path.unlink(missing_ok=True)


@router.get("/export")
def export_taxonomy() -> dict[str, str]:
path = export_workbook(Path("data/taxonomy/exports/taxonomy.xlsx"))
Expand Down
9 changes: 7 additions & 2 deletions backend/app/services/taxonomy_workbench_service.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from pathlib import Path

from argument_risk_engine.taxonomy.exporter import export_taxonomy_excel
from argument_risk_engine.taxonomy.importer import import_taxonomy_excel
from argument_risk_engine.taxonomy.importer import import_taxonomy_excel, import_workbook as import_taxonomy_workbook
from argument_risk_engine.taxonomy.validator import validate_taxonomy_pack

from backend.app.services.taxonomy_service import get_active_pack, save_active_pack
Expand All @@ -13,9 +13,14 @@ def quality() -> dict[str, object]:


def import_workbook(path: Path) -> dict[str, object]:
report = import_taxonomy_workbook(path)
pack = import_taxonomy_excel(path)
save_active_pack(pack)
return {"entry_count": len(pack.entries)}
return {
"entry_count": len(pack.entries),
"errors": [issue.message for issue in report.errors],
"warnings": [issue.message for issue in report.warnings],
}


def export_workbook(path: Path) -> Path:
Expand Down
99 changes: 95 additions & 4 deletions data/config/model_profiles.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,95 @@
profiles:
deterministic:
provider: deterministic
model: local-keyword
{
"version": "0.2.0",
"items": [
{
"provider_id": "deterministic_baseline",
"label": "Deterministic Baseline",
"provider_type": "deterministic",
"base_url": "",
"api_key_env_var": "",
"default_model": "",
"timeout_seconds": "30",
"max_tokens": "0",
"temperature": "0",
"supports_json_mode": "n/a",
"supports_streaming": "n/a",
"enabled_default": "yes",
"security_note": "No API key required; must always work offline."
},
{
"provider_id": "lm_studio_local",
"label": "LM Studio Local",
"provider_type": "openai_compatible",
"base_url": "http://localhost:1234/v1",
"api_key_env_var": "LM_STUDIO_API_KEY",
"default_model": "local-model",
"timeout_seconds": "60",
"max_tokens": "2048",
"temperature": "0",
"supports_json_mode": "unknown",
"supports_streaming": "yes",
"enabled_default": "no",
"security_note": "Dashboard must not store raw key; local servers may accept dummy key."
},
{
"provider_id": "ollama_local",
"label": "Ollama Local",
"provider_type": "openai_compatible",
"base_url": "http://localhost:11434/v1",
"api_key_env_var": "OLLAMA_API_KEY",
"default_model": "qwen3:8b",
"timeout_seconds": "60",
"max_tokens": "2048",
"temperature": "0",
"supports_json_mode": "unknown",
"supports_streaming": "yes",
"enabled_default": "no",
"security_note": "Use OpenAI-compatible endpoint; avoid assuming JSON mode."
},
{
"provider_id": "openai_remote",
"label": "OpenAI Remote",
"provider_type": "openai_compatible",
"base_url": "https://api.openai.com/v1",
"api_key_env_var": "OPENAI_API_KEY",
"default_model": "gpt-4.1-mini",
"timeout_seconds": "60",
"max_tokens": "2048",
"temperature": "0",
"supports_json_mode": "yes",
"supports_streaming": "yes",
"enabled_default": "no",
"security_note": "Backend reads key from environment variable only."
},
{
"provider_id": "openrouter_remote",
"label": "OpenRouter Remote",
"provider_type": "openai_compatible",
"base_url": "https://openrouter.ai/api/v1",
"api_key_env_var": "OPENROUTER_API_KEY",
"default_model": "",
"timeout_seconds": "60",
"max_tokens": "2048",
"temperature": "0",
"supports_json_mode": "model_dependent",
"supports_streaming": "yes",
"enabled_default": "no",
"security_note": "Treat as OpenAI-compatible; do not hardcode model names."
},
{
"provider_id": "custom_openai_compatible",
"label": "Custom OpenAI-Compatible",
"provider_type": "openai_compatible",
"base_url": "",
"api_key_env_var": "CUSTOM_LLM_API_KEY",
"default_model": "",
"timeout_seconds": "60",
"max_tokens": "2048",
"temperature": "0",
"supports_json_mode": "unknown",
"supports_streaming": "unknown",
"enabled_default": "no",
"security_note": "User-configurable base_url and model_name; never expose raw key."
}
]
}
Empty file added data/reports/.gitkeep
Empty file.
169 changes: 169 additions & 0 deletions data/reports/taxonomy_validation_report.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
{
"ok": true,
"entry_count": 1100,
"active_classification_count": 46,
"errors": [],
"warnings": [
{
"code": "high_fp_active",
"message": "tu_quoque_canonical has high false-positive sensitivity and should normally remain review_required",
"severity": "warning",
"entry_id": "tu_quoque_canonical",
"row_number": 6
},
{
"code": "high_fp_active",
"message": "guilt_by_association_canonical has high false-positive sensitivity and should normally remain review_required",
"severity": "warning",
"entry_id": "guilt_by_association_canonical",
"row_number": 7
},
{
"code": "high_fp_active",
"message": "genetic_fallacy_canonical has high false-positive sensitivity and should normally remain review_required",
"severity": "warning",
"entry_id": "genetic_fallacy_canonical",
"row_number": 8
},
{
"code": "high_fp_active",
"message": "appeal_to_authority_misuse_canonical has high false-positive sensitivity and should normally remain review_required",
"severity": "warning",
"entry_id": "appeal_to_authority_misuse_canonical",
"row_number": 9
},
{
"code": "high_fp_active",
"message": "appeal_to_emotion_canonical has high false-positive sensitivity and should normally remain review_required",
"severity": "warning",
"entry_id": "appeal_to_emotion_canonical",
"row_number": 10
},
{
"code": "high_fp_active",
"message": "appeal_to_fear_canonical has high false-positive sensitivity and should normally remain review_required",
"severity": "warning",
"entry_id": "appeal_to_fear_canonical",
"row_number": 11
},
{
"code": "high_fp_active",
"message": "appeal_to_pity_canonical has high false-positive sensitivity and should normally remain review_required",
"severity": "warning",
"entry_id": "appeal_to_pity_canonical",
"row_number": 12
},
{
"code": "high_fp_active",
"message": "appeal_to_force_canonical has high false-positive sensitivity and should normally remain review_required",
"severity": "warning",
"entry_id": "appeal_to_force_canonical",
"row_number": 13
},
{
"code": "high_fp_active",
"message": "appeal_to_popularity_canonical has high false-positive sensitivity and should normally remain review_required",
"severity": "warning",
"entry_id": "appeal_to_popularity_canonical",
"row_number": 14
},
{
"code": "high_fp_active",
"message": "red_herring_canonical has high false-positive sensitivity and should normally remain review_required",
"severity": "warning",
"entry_id": "red_herring_canonical",
"row_number": 28
},
{
"code": "high_fp_active",
"message": "moving_goalposts_canonical has high false-positive sensitivity and should normally remain review_required",
"severity": "warning",
"entry_id": "moving_goalposts_canonical",
"row_number": 30
},
{
"code": "high_fp_active",
"message": "no_true_scotsman_canonical has high false-positive sensitivity and should normally remain review_required",
"severity": "warning",
"entry_id": "no_true_scotsman_canonical",
"row_number": 31
},
{
"code": "high_fp_active",
"message": "special_pleading_canonical has high false-positive sensitivity and should normally remain review_required",
"severity": "warning",
"entry_id": "special_pleading_canonical",
"row_number": 32
},
{
"code": "high_fp_active",
"message": "burden_of_proof_shift_canonical has high false-positive sensitivity and should normally remain review_required",
"severity": "warning",
"entry_id": "burden_of_proof_shift_canonical",
"row_number": 33
},
{
"code": "high_fp_active",
"message": "post_hoc_canonical has high false-positive sensitivity and should normally remain review_required",
"severity": "warning",
"entry_id": "post_hoc_canonical",
"row_number": 35
},
{
"code": "high_fp_active",
"message": "cum_hoc_canonical has high false-positive sensitivity and should normally remain review_required",
"severity": "warning",
"entry_id": "cum_hoc_canonical",
"row_number": 36
},
{
"code": "high_fp_active",
"message": "appeal_to_nature_canonical has high false-positive sensitivity and should normally remain review_required",
"severity": "warning",
"entry_id": "appeal_to_nature_canonical",
"row_number": 41
},
{
"code": "high_fp_active",
"message": "appeal_to_tradition_canonical has high false-positive sensitivity and should normally remain review_required",
"severity": "warning",
"entry_id": "appeal_to_tradition_canonical",
"row_number": 42
},
{
"code": "high_fp_active",
"message": "appeal_to_novelty_canonical has high false-positive sensitivity and should normally remain review_required",
"severity": "warning",
"entry_id": "appeal_to_novelty_canonical",
"row_number": 43
},
{
"code": "high_fp_active",
"message": "nirvana_fallacy_canonical has high false-positive sensitivity and should normally remain review_required",
"severity": "warning",
"entry_id": "nirvana_fallacy_canonical",
"row_number": 44
},
{
"code": "high_fp_active",
"message": "middle_ground_fallacy_canonical has high false-positive sensitivity and should normally remain review_required",
"severity": "warning",
"entry_id": "middle_ground_fallacy_canonical",
"row_number": 45
},
{
"code": "high_fp_active",
"message": "relative_privation_canonical has high false-positive sensitivity and should normally remain review_required",
"severity": "warning",
"entry_id": "relative_privation_canonical",
"row_number": 46
},
{
"code": "high_fp_active",
"message": "whataboutism_canonical has high false-positive sensitivity and should normally remain review_required",
"severity": "warning",
"entry_id": "whataboutism_canonical",
"row_number": 47
}
]
}
Empty file added data/taxonomy/exports/.gitkeep
Empty file.
Empty file added data/taxonomy/imports/.gitkeep
Empty file.
Loading