An open API service indexing awesome lists of open source software.

https://github.com/igorjs/advisor


https://github.com/igorjs/advisor

Last synced: about 6 hours ago
JSON representation

Awesome Lists containing this project

README

          

# Advisor

> **WARNING: THIS IS A TEST/DEMONSTRATION PROJECT.**
>
> This application is built as an experiment and **MUST NOT** be used as
> a source of real financial advice. The strategies, recommendations, and records
> generated by this software are produced by a large language model (LLM) and are
> for **demonstration purposes only**.
>
> **Use at your own risk.**

## Exclusion of Liability

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED. THE AUTHORS AND COPYRIGHT HOLDERS SHALL NOT BE LIABLE FOR ANY CLAIM,
DAMAGES, OR OTHER LIABILITY ARISING FROM THE USE OF THIS SOFTWARE.

IN PARTICULAR:

- **No financial advice.** Nothing produced by this application constitutes
financial, investment, legal, tax, or other professional advice.
- **No fiduciary relationship.** Use of this software does not create any
advisory, fiduciary, or professional-services relationship.
- **No accuracy guarantee.** LLM-generated content may be inaccurate,
incomplete, outdated, or entirely fabricated. Do not rely on it for any
real-world decision.
- **No liability for losses.** The authors accept no responsibility for any
financial loss, damage, or other harm resulting from the use of, or reliance
on, this software or its outputs.

By using this software you acknowledge and accept these terms. If you do not
agree, do not use this software.

For the full license terms, see [LICENSE](./LICENSE).

---

Full-stack agentic LLM advisor. Start a conversation, the AI agent interviews
you about your client's situation, researches the web, and produces structured
advisory records that you can review, edit, and manage.

## Prerequisites

- [Node.js](https://nodejs.org/) >= 26 (LTS)
- [pnpm](https://pnpm.io/) >= 11

## Quick Start

```bash
# Install dependencies
pnpm install

# Set up environment
cp .env.example .env
# Edit .env with your API keys (see Environment below)

# Run database migrations
pnpm db:migrate

# Start development servers (client + server)
pnpm dev
```

The client runs at [http://localhost:5173](http://localhost:5173) and proxies
API requests to the server on port 3001.

## Environment

| Variable | Required | Description |
| -------------------- | -------- | ----------------------------------------------- |
| `LLM_API_KEY` | Yes | API key for the LLM provider |
| `LLM_BASE_URL` | No | OpenAI-compatible base URL (defaults to OpenAI) |
| `LLM_MODEL` | No | Model identifier (defaults to `gpt-4o-mini`) |
| `JINA_API_KEY` | Yes | Jina Search API key for web research |
| `TURSO_DATABASE_URL` | No | Turso remote database URL |
| `TURSO_AUTH_TOKEN` | No | Turso authentication token |
| `RATE_LIMIT_MAX` | No | Requests per minute per IP (defaults to 100) |

### API Keys Setup

The app requires two API keys: one for the LLM and one for web search.

**1. LLM provider (OpenRouter recommended)**

OpenRouter gives access to multiple models (GPT-4o, Claude, Llama, etc.)
through a single API key with pay-as-you-go pricing.

1. Create an account at [openrouter.ai](https://openrouter.ai/)
2. Go to [Keys](https://openrouter.ai/keys) and click "Create Key"
3. Copy the key and add to your `.env`:

```env
LLM_API_KEY=sk-or-v1-your-key-here
LLM_BASE_URL=https://openrouter.ai/api/v1
LLM_MODEL=openai/gpt-4o-mini
```

Alternatively, use OpenAI directly:

1. Create an account at [platform.openai.com](https://platform.openai.com/)
2. Go to [API Keys](https://platform.openai.com/api-keys) and create a key
3. Add to `.env` (no `LLM_BASE_URL` needed, defaults to OpenAI):

```env
LLM_API_KEY=sk-your-key-here
LLM_MODEL=gpt-4o-mini
```

**2. Jina Search API (web research)**

The AI agent uses Jina to search the web for current tax rates and legislation.

1. Create an account at [jina.ai](https://jina.ai/)
2. Go to [API Keys](https://jina.ai/api-key) and generate a key
3. Add to `.env`:

```env
JINA_API_KEY=jina_your-key-here
```

**3. Turso database (optional)**

The app uses a local SQLite file by default, no setup needed. For
production/edge deployment, Turso provides a hosted SQLite-compatible
database with automatic replication.

1. Create an account at [turso.tech](https://turso.tech/)
2. Install the CLI: `brew install tursodatabase/tap/turso` (or see [docs](https://docs.turso.tech/cli/installation))
3. Authenticate: `turso auth login`
4. Create a database: `turso db create advisor`
5. Get the connection URL: `turso db show advisor --url`
6. Create an auth token: `turso db tokens create advisor`
7. Add to `.env`:

```env
TURSO_DATABASE_URL=libsql://advisor-your-username.turso.io
TURSO_AUTH_TOKEN=your-auth-token
```

When both are set, the app runs as an embedded replica with automatic sync.
When absent, it falls back to `file:data/advisor.db` (zero config).

## Architecture

```
advisor/
├── client/ # React 19 SPA (Vite + Tailwind CSS 4)
│ └── src/
│ ├── api/ # API client functions
│ ├── components/ # Chat, records, forms, kbd hints, theme toggle
│ ├── hooks/ # useChatStream, useConversationId, useHotkey
│ ├── lib/ # Error toast helpers
│ ├── locales/ # i18n translation files
│ └── types/ # Shared type definitions
├── server/ # Hono API server
│ └── src/
│ ├── config/ # LLM, rate-limit, search, system prompt
│ ├── db/ # Drizzle ORM schema and migrations
│ ├── dto/ # Data transfer objects (conversation, record, message)
│ ├── lib/ # Result/Option types, HTTP helpers, extractRecords
│ ├── middleware/ # CORS, security, rate-limiter, idempotency, logging
│ ├── routes/ # HTTP route handlers (conversations, records, chat)
│ └── services/ # Agent loop, conversation, record, LLM, search
├── e2e/ # Playwright end-to-end tests
├── docs/dev/ # Design decisions and implementation plan
└── package.json # Root workspace configuration
```

### Key Design Decisions

- **Agentic interview:** The AI asks one clarifying question at a time before
producing strategies. This creates a natural conversation flow rather than
dumping questions on the user.
- **Two-phase records extraction:** If the LLM returns prose instead of JSON,
a focused second call with `response_format: json_object` converts it to
structured records. Handles model non-compliance gracefully.
- **SSE streaming:** Agent events (searching, responding, records) stream to
the client in real-time via Server-Sent Events.
- **Errors as values:** Services return `Result` instead of
throwing. Routes use a `matchResult()` helper.
- **Message persistence:** Chat messages are stored in the DB and hydrated on
page refresh via the conversation API. A deterministic sentinel (`[records:N]`)
is stored for records-producing messages and rendered as localised text by
the client.
- **Dark mode:** System preference detection on load, session-only toggle.
Class-based via `@variant dark` in Tailwind CSS 4 with no flash of wrong
theme (inline script in ``).
- **Zero type assertions:** No `as` casts, no `!` non-null assertions. Type
guards and null checks throughout.
- **Functional core:** Result/Option types inspired by pure-fx, strict
TypeScript 6, `null` over `undefined`.

## Tech Stack

- **Frontend:** React 19, @tanstack/react-query, Tailwind CSS 4, react-i18next, sonner
- **Backend:** Hono, Drizzle ORM, Turso/libSQL, OpenAI SDK, Pino, zod 4
- **Testing:** Vitest (88 unit/integration tests), Playwright (30 e2e tests), axe-core accessibility
- **Language:** TypeScript 6 (strict mode, no `any`, no `as` assertions)

## API Endpoints

```
GET /api/health Health check

POST /api/v1/conversations Create a conversation
GET /api/v1/conversations/:id Get conversation with records + messages
PATCH /api/v1/conversations/:id Re-query with updated text

POST /api/v1/conversations/:id/chat Send message (SSE stream)
POST /api/v1/conversations/:id/edit/:messageId Edit message and re-run (SSE stream)

PATCH /api/v1/conversations/:id/records/:recordId Update a record
DELETE /api/v1/conversations/:id/records/:recordId Delete a record
```

## Scripts

| Command | Description |
| ------------------ | ------------------------------------------------ |
| `pnpm dev` | Start both client and server in development mode |
| `pnpm test` | Run all unit and integration tests (Vitest) |
| `pnpm test:e2e` | Run e2e tests headed (Playwright, needs server) |
| `pnpm test:e2e:ci` | Run e2e tests headless (for CI pipelines) |
| `pnpm test:watch` | Run server tests in watch mode |
| `pnpm build` | Build both client and server |
| `pnpm db:generate` | Generate a new database migration |
| `pnpm db:migrate` | Apply pending migrations |

## Testing

```bash
# Unit + integration tests (fast, no server needed)
pnpm test

# End-to-end tests headed (starts dev servers, real LLM calls)
pnpm test:e2e

# End-to-end tests headless (CI)
pnpm test:e2e:ci

# Watch mode for TDD
pnpm test:watch
```

### Unit and Integration Tests (88 tests)

- **Unit:** Result/Option library, extractRecords JSON parsing, conversation DTO message filtering
- **Middleware:** Error handler status mapping, rate limiter behaviour
- **Service:** Conversation CRUD, record CRUD, re-query atomicity, LLM failure propagation
- **API Integration:** Full HTTP request lifecycle via Hono `app.request()`, including records PATCH/DELETE routes

### End-to-End Tests (30 tests, Playwright + axe-core)

- **Landing page:** Title, subtitle, form validation, submit button states
- **Conversation flow:** Prompt submission, URL routing, AI responses, follow-up messages
- **Records panel:** Multi-turn interview to records, edit/delete buttons
- **Persistence:** Message hydration on page refresh, sentinel rendering
- **Navigation:** New chat, URL routing, error states
- **Input UX:** Enter for newlines, Cmd+Enter to submit, focus management
- **Dark mode:** System preference detection, toggle, visibility on both views
- **Accessibility:** axe-core audit (critical + serious) on landing, chat, and dark mode
- **Keyboard navigation:** `/` focus, Escape cancel, Cmd+Enter submit, Tab traversal

## Design Docs

- [Requirements](docs/dev/requirements.md) -- Initial project requirements
- [Design Decisions](docs/dev/design.md) -- Architecture, API design, error handling philosophy
- [Implementation Plan](docs/dev/implementation.md) -- File structure, build order, testing approach

## License

This project is licensed under the
[GNU Affero General Public License v3.0](./LICENSE)
(SPDX: `AGPL-3.0-only`).

See the [LICENSE](./LICENSE) file for the full license text.