Go to file
lehel 1484b519d7
openai call
2025-10-08 22:18:40 +02:00
.gitea/workflows gitea support 2025-10-01 17:03:09 +02:00
.github/workflows gitignore 2025-10-01 14:09:35 +02:00
.idea add ability to save 2025-10-07 10:04:03 +02:00
config embedding db 2025-10-08 21:04:33 +02:00
migrations rewriten migration 2025-10-08 21:58:29 +02:00
scripts reorganise files 2025-10-07 21:47:59 +02:00
web_templates reorganise files 2025-10-07 21:47:59 +02:00
.gitignore remove vendor 2025-10-07 17:26:12 +02:00
Dockerfile reorganise files 2025-10-07 21:47:59 +02:00
Makefile embedding db 2025-10-08 21:04:33 +02:00
README.md gitea support 2025-10-01 17:03:09 +02:00
auth_test.go login handler 2025-10-07 17:41:19 +02:00
chat_service.go separate clients 2025-10-08 13:17:56 +02:00
chat_service_integration_test.go tests 2025-10-08 15:09:36 +02:00
config.go new table + translate call 2025-10-08 15:08:35 +02:00
controller_test.go add test 2025-10-08 14:23:14 +02:00
controllers.go reorganise files 2025-10-07 21:47:59 +02:00
db.go reorganise files 2025-10-07 21:47:59 +02:00
go.mod login handler 2025-10-07 17:41:19 +02:00
go.sum remove vendor 2025-10-07 17:26:12 +02:00
handlechat_integration_test.go tests 2025-10-08 15:09:36 +02:00
llm.go embedding db 2025-10-08 21:04:33 +02:00
llm_test.go tests 2025-10-08 15:09:36 +02:00
log.go db editor for admin page 2025-10-01 21:27:31 +02:00
main.go embedding db 2025-10-08 21:04:33 +02:00
models.go remove vendor 2025-10-07 17:26:12 +02:00
ollama_client.go embedding db 2025-10-08 21:04:33 +02:00
openai_client.go openai call 2025-10-08 22:18:40 +02:00
openrouter_integration_test.go add test 2025-10-08 14:23:14 +02:00
repository.go rewriten migration 2025-10-08 21:58:29 +02:00
sentence_embeddings.go embedding db 2025-10-08 21:04:33 +02:00
ui.go admin chat ui 2025-10-02 10:48:20 +02:00

README.md

Vetrag

Lightweight veterinary visit reasoning helper with LLM-assisted keyword extraction and disambiguation.

Features

  • Switch seamlessly between local Ollama and OpenRouter (OpenAI-compatible) LLM backends by changing environment variables only.
  • Structured JSON outputs enforced using provider-supported response formats (Ollama format, OpenAI/OpenRouter response_format: { type: json_object }).
  • Integration tests using mock LLM & DB (no network dependency).
  • GitHub Actions CI (vet, test, build).
  • Gitea Actions CI parity (same steps mirrored under .gitea/workflows/ci.yml).

Quick Start

1. Clone & build

git clone <repo-url>
cd vetrag
go build ./...

2. Prepare data

Ensure config.yaml and maindb.yaml / db.yaml exist as provided. Visit data is loaded at runtime (see models.go / db.go).

3. Run with Ollama (local)

Pull or have a model available (example: ollama pull qwen2.5):

export OPENAI_BASE_URL=http://localhost:11434/api/chat
export OPENAI_MODEL=qwen2.5:latest
# API key not required for Ollama
export OPENAI_API_KEY=

go run .

4. Run with OpenRouter

Sign up at https://openrouter.ai and get an API key.

export OPENAI_BASE_URL=https://openrouter.ai/api/v1/chat/completions
export OPENAI_API_KEY=sk-or-XXXXXXXXXXXXXXXX
export OPENAI_MODEL=meta-llama/llama-3.1-70b-instruct  # or any supported model

go run .

Open http://localhost:8080/ in your browser.

5. Health & Chat

curl -s http://localhost:8080/health
curl -s -X POST http://localhost:8080/chat -H 'Content-Type: application/json' -d '{"message":"my dog has diarrhea"}' | jq

Environment Variables

Variable Purpose Default (if empty)
OPENAI_BASE_URL LLM endpoint (Ollama chat or OpenRouter chat completions) http://localhost:11434/api/chat
OPENAI_API_KEY Bearer token for OpenRouter/OpenAI-style APIs (unused if empty)
OPENAI_MODEL Model identifier (Ollama model tag or OpenRouter model slug) none (must set for remote)

How Backend Selection Works

llm.go auto-detects the style:

  • If the base URL contains openrouter.ai or /v1/ it uses OpenAI-style request & parses choices[0].message.content.
  • Otherwise it assumes Ollama and posts to /api/chat with format for structured JSON.

Structured Output

We define a JSON Schema-like map internally and:

  • Ollama: send as format (native structured output extension).
  • OpenRouter/OpenAI: send response_format: { type: "json_object" } plus a system instruction describing the expected keys.

Prompts

Prompts in config.yaml have been adjusted to explicitly demand JSON only. This reduces hallucinated prose and plays well with both backends.

Testing

Run:

go test ./...

All tests mock the LLM so no network is required.

CI

GitHub Actions workflow at .github/workflows/ci.yml runs vet, tests, build on push/PR.

Gitea Actions Support

A mirrored workflow exists at .gitea/workflows/ci.yml so self-hosted Gitea instances (with Actions/act_runner) execute the same pipeline:

  • Triggers: push to main / master, pull requests, manual dispatch.
  • Steps: checkout, Go setup (from go-version-file), vet, test, build.
  • Default dummy env vars (override with repository secrets if needed).

To add real secrets in Gitea (names should match those used in code):

  1. In the repository UI: Settings → Secrets → Actions (menu names may differ slightly by Gitea version).
  2. Add OPENAI_API_KEY (optional for OpenRouter usage).
  3. (Optional) Add OPENAI_MODEL / OPENAI_BASE_URL overrides if you want CI integration tests against a live endpoint (currently tests are mocked and do not require network access).

If you later add integration tests that call a real provider, modify the workflow:

env:
  OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
  OPENAI_BASE_URL: https://openrouter.ai/api/v1/chat/completions
  OPENAI_MODEL: meta-llama/llama-3.1-70b-instruct

Make sure rate limits and billing are acceptable before enabling real calls.

Runner notes:

  • Ensure your Gitea Actions runner image includes Git and Go toolchain or relies on actions/setup-go (supported in newer Gitea releases with remote action fetching).
  • If remote action fetching is restricted, vendor the actions by mirroring their repositories or replace with inline shell steps (fallback approach).

Troubleshooting

Symptom Cause Fix
Provider error referencing response_format and json_schema Some providers reject json_schema We now default to json_object; ensure you pulled latest changes.
Empty response Model returned non-JSON or empty content Enable debug logs (see below) and inspect raw response.
Non-JSON content (code fences) Model ignored instruction Try a stricter system message or switch to a model with better JSON adherence.

Enable Debug Logging

Temporarily edit main.go:

logrus.SetLevel(logrus.DebugLevel)

(You can also refactor later to read a LOG_LEVEL env var.)

Sanitizing Output (Optional Future Improvement)

If some models wrap JSON in text, a post-processor could strip code fences and re-parse. Not implemented yet to keep logic strict.

Next Ideas

  • Add retry with exponential backoff for transient 5xx.
  • Add optional json fallback if a provider rejects json_object.
  • Add streaming support.
  • Add integration test with recorded OpenRouter fixture.

License

(Choose and add a LICENSE file if planning to open source.)