{% extends "base.html" %} {% block title %}Config — dvad{% endblock %} {% block content %}
Binary: {{ dvad_binary }}
Config: {{ config_file }}
Data: {{ data_dir }}
Reviews: {{ reviews_dir }}
Logs: {{ logs_dir }}
Templates: {{ templates_dir }}
{# ── Structured Tab ─────────────────────────────────────────────────── #}

Models

{% if config and sorted_models %} {# -- Local model cards (rendered separately with distinct styling) -- #} {% for name, m in sorted_models %} {% if m.provider == 'local' %}
{{ name }} local FREE
provider: local (llama.cpp)
model_id: {{ m.model_id }}
api_key_env: {{ m.api_key_env if m.api_key_env else 'none (no auth required)' }}
api_base: {{ m.api_base }} - configure your own endpoint
context: {{ "{:,}".format(m.context_window) if m.context_window else 'unset' }} timeout: {{ m.timeout }}s max out: {{ "{:,}".format(m.max_out_configured) if m.max_out_configured else 'unset' }} /{{ "{:,}".format(m.max_out_stated) if m.max_out_stated else '?' }}
cost in: 0 cost out: 0 per 1K tokens (local GPU)

Devil's Advocate can run entirely on open-weight models. Commercial API access not required. Any OpenAI-compatible endpoint works. Assign local models to every review role for a self-hosted, enterprise-controlled deployment.

A low parameter, quantized model will produce noticeably weaker adversarial review than state of the art commercial models. This local model is provided to demonstrate flexibility.

{% endif %} {% endfor %} {# -- Commercial model cards -- #} {% for name, m in sorted_models %} {% if m.provider != 'local' %}
{{ name }}{% if m.enabled is defined and not m.enabled %} disabled{% endif %} {% set tier = model_cost_tiers.get(name, 1) %} {{ '$' * tier }}
provider: {{ m.provider }}
model_id: {{ m.model_id }}
api_key_env: {{ m.api_key_env }}
{% if m.api_base %}
api_base: {{ m.api_base }}
{% endif %}
context: {{ "{:,}".format(m.context_window) if m.context_window else 'unset' }} timeout: {{ m.timeout }}s max out: {{ m.max_out_configured if m.max_out_configured else 'unset' }} /{{ "{:,}".format(m.max_out_stated) if m.max_out_stated else '?' }}
cost in: {{ m.cost_per_1k_input if m.cost_per_1k_input is not none else 'unset' }} cost out: {{ m.cost_per_1k_output if m.cost_per_1k_output is not none else 'unset' }} per 1K tokens
{% if m.use_completion_tokens %}
use_completion_tokens: true
{% endif %}
{% endif %} {% endfor %} {% else %}

No models configured.

{% endif %}

API Keys

Keys are saved to {{ env_file_path }} {% if env_file_exists %} exists {% else %} not created {% endif %}

Loading...

Validation

{% if issues %} {% for level, msg in issues %}
{{ level|upper }}: {{ msg }}
{% endfor %} {% else %}
Configuration is valid.
{% endif %}

Role Assignments

{% set role_keys = ['author', 'reviewer1', 'reviewer2', 'dedup', 'normalization', 'revision', 'integration'] %} {% for r in role_assignments %}
{{ r.label }}: {% if r.model %} {{ r.model }} {% else %} - {% endif %}
{% endfor %}
What do these roles do?
Author: Generates initial responses to reviewer findings and produces revised artifacts
Reviewer (x2): Independently analyzes input for issues, risks, and improvements
Dedup: Consolidates overlapping findings from multiple reviewers into groups
Normalization: Standardizes severity levels and categories across grouped findings
Revision: Creates the final revised artifact incorporating accepted feedback
Integration: Reviews cross-system integration concerns and component interactions

Development

live e2e tests: {{ 'enabled' if settings.get('live_testing') else 'disabled' }} run without -m live flag
{# ── Save Roles Toast ───────────────────────────────────────────────── #}
{# ── Raw YAML Tab ───────────────────────────────────────────────────── #} {% endblock %} {% block scripts %} {% endblock %}