docs: update AGENTS.md and README.md with accurate project details
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
- Root AGENTS.md: add build pipeline, Makefile targets, full directory listing - Backend AGENTS.md: add architecture layout, API routes table, streaming/store patterns, missing deps (jsonschema-go, values pkg, types pkg) - Frontend AGENTS.md: add architecture layout, missing deps (marked, highlight.js), Alpine component pattern, build pipeline details - README.md: add env var config table, Docker/Make workflows, dev setup, thinking support, token stats, structured output, llama.cpp timings
This commit is contained in:
26
AGENTS.md
26
AGENTS.md
@@ -5,15 +5,31 @@ This repository is a **monorepo** with two main packages that can be built and r
|
|||||||
## Directory Structure
|
## Directory Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
├── frontend/ # Client‑side TypeScript application
|
├── frontend/ # TypeScript + Alpine.js + Tailwind CSS client
|
||||||
├── backend/ # Server‑side Go micro‑service
|
├── backend/ # Go HTTP server with embedded frontend assets
|
||||||
├── .envrc, flake.nix, README.md
|
├── Makefile # Orchestrates frontend → backend build pipeline
|
||||||
|
├── Dockerfile # Multi-stage Docker build
|
||||||
|
├── .drone.yml # CI pipeline (tests + Docker publish)
|
||||||
|
├── flake.nix # Nix dev shell (Go, Bun, LSPs, linters)
|
||||||
└── …
|
└── …
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Build Pipeline
|
||||||
|
|
||||||
|
The frontend builds to `frontend/public/dist/`, then `make frontend` copies the full `frontend/public/` tree into `backend/web/static/` where it gets embedded into the Go binary via `//go:embed`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make all # Build frontend + backend
|
||||||
|
make frontend # Build frontend, copy to backend/web/static/
|
||||||
|
make backend # Build Go binary (requires frontend assets)
|
||||||
|
make dev # Run both with hot-reload
|
||||||
|
make tests # Run Go tests
|
||||||
|
make docker # Build Docker image
|
||||||
|
```
|
||||||
|
|
||||||
## Package Details
|
## Package Details
|
||||||
|
|
||||||
See package‑specific instructions:
|
See package‑specific instructions:
|
||||||
|
|
||||||
- **frontend/** - `@frontend/AGENTS.md`
|
- **frontend/** — `@frontend/AGENTS.md`
|
||||||
- **backend/** - `@backend/AGENTS.md`
|
- **backend/** — `@backend/AGENTS.md`
|
||||||
|
|||||||
114
README.md
114
README.md
@@ -1,100 +1,108 @@
|
|||||||
# Aethera
|
# Aethera
|
||||||
|
|
||||||
A sophisticated web dashboard for AI-powered conversations and image generation with chat interface, multiple conversations, and local storage capabilities.
|
A web dashboard for AI-powered conversations and image generation, backed by any OpenAI-compatible API.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- **Chat Interface**: Engage with AI models through a clean, responsive chat interface
|
- **Chat Interface** — streaming responses with Markdown rendering and syntax highlighting
|
||||||
- **Multiple Conversations**: Switch between different conversation threads
|
- **Thinking Support** — displays model reasoning/thinking content when available
|
||||||
- **Image Generation**: Create and manage AI-generated images with customizable prompts
|
- **Multiple Conversations** — switch between threads with auto-generated titles
|
||||||
- **Theme Support**: Toggle between light and dark modes
|
- **Image Generation & Editing** — create and edit images with customizable prompts, masks, and seeds
|
||||||
- **Local Storage**: All conversations and images are stored locally on your system
|
- **Token Statistics** — real-time prompt/generation throughput and timing metrics
|
||||||
- **Markdown Rendering**: View beautifully formatted responses with syntax highlighting
|
- **Theme Support** — light and dark mode toggle
|
||||||
|
- **Structured Output** — JSON schema-based structured responses from models
|
||||||
|
- **Embedded Frontend** — single binary deployment with assets compiled in
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
- Go 1.25.5 or later
|
- Go 1.25.5+
|
||||||
- Bun package manager
|
- Bun
|
||||||
- An OpenAI-compatible API endpoint (OpenAI, local LLM, etc.)
|
- An OpenAI-compatible API endpoint
|
||||||
|
|
||||||
### Installation
|
### Using Make
|
||||||
|
|
||||||
1. Clone the repository:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone <repository-url>
|
make all # Build frontend + backend
|
||||||
cd aethera
|
./backend/dist/aethera
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Build the backend:
|
### Using Docker
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd backend
|
make docker
|
||||||
go build -o ./dist/aethera ./cmd
|
docker run -p 8080:8080 -v aethera-data:/app/data aethera
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Build the frontend:
|
### Manual Build
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd ../frontend
|
# Frontend
|
||||||
bun run build
|
cd frontend && bun install && bun run build && cd ..
|
||||||
```
|
|
||||||
|
|
||||||
### Running the Application
|
# Copy assets to backend
|
||||||
|
mkdir -p backend/web/static
|
||||||
|
cp -R frontend/public/. backend/web/static/
|
||||||
|
|
||||||
Start the server from the backend directory:
|
# Backend
|
||||||
|
cd backend && go build -o ./dist/aethera ./cmd
|
||||||
```bash
|
|
||||||
./dist/aethera
|
./dist/aethera
|
||||||
```
|
```
|
||||||
|
|
||||||
By default, the application runs at `http://localhost:8080`
|
Open `http://localhost:8080` in your browser.
|
||||||
|
|
||||||
Open your browser and navigate to the URL to begin using Aethera.
|
## Configuration
|
||||||
|
|
||||||
## Configuration Options
|
Configuration is available via CLI flags and environment variables (prefixed `AETHERA_`):
|
||||||
|
|
||||||
You can customize the server behavior with these command-line flags:
|
| Flag | Env Var | Default | Description |
|
||||||
|
|----------------|---------------------|-------------|--------------------------------------------|
|
||||||
- `--data-dir`: Directory for storing generated images (default: `data`)
|
| `--data-dir` | `AETHERA_DATA_DIR` | `./data` | Directory for chats, settings, and images |
|
||||||
- `--static-dir`: Directory to serve frontend files from instead of embedded assets (useful for development)
|
| `--static-dir` | `AETHERA_STATIC_DIR`| *(embedded)*| Serve frontend from disk (for development) |
|
||||||
- `--listen`: Address to listen on (default: `localhost`)
|
| `--listen` | `AETHERA_LISTEN` | `localhost` | Listen address |
|
||||||
- `--port`: Port to listen on (default: `8080`)
|
| `--port` | `AETHERA_PORT` | `8080` | Listen port |
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
./dist/aethera --port 3000 --listen 0.0.0.0
|
./backend/dist/aethera --port 3000 --listen 0.0.0.0
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
A Nix flake is provided for the development environment:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix develop # or use direnv with .envrc
|
||||||
|
```
|
||||||
|
|
||||||
|
This provides Go, Bun, `gopls`, `typescript-language-server`, `golangci-lint`, and `watchman`.
|
||||||
|
|
||||||
|
For hot-reload development:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make dev
|
||||||
|
```
|
||||||
|
|
||||||
|
This starts the Go backend (serving frontend from disk) and the frontend in watch mode concurrently.
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
1. **Configure Your API**: Navigate to the Settings page and enter your OpenAI-compatible API endpoint URL
|
1. **Configure Your API** — navigate to Settings and enter your OpenAI-compatible API endpoint URL
|
||||||
2. **Start Chatting**: Use the Chat interface to begin conversations with your AI model
|
2. **Start Chatting** — use the Chat interface to begin conversations
|
||||||
3. **Generate Images**: Visit the Images page to create images using text prompts
|
3. **Generate Images** — visit the Images page to create or edit images
|
||||||
4. **Manage Your Content**: View and delete images, organize conversations
|
4. **Manage Content** — view, delete, and organize conversations and images
|
||||||
|
|
||||||
## Supported AI Services
|
## Supported AI Services
|
||||||
|
|
||||||
Aethera works with any OpenAI-compatible API, including:
|
Aethera works with any OpenAI-compatible API, including:
|
||||||
|
|
||||||
- OpenAI
|
- OpenAI
|
||||||
- Local LLMs (Ollama, LocalAI, etc.)
|
- Local LLMs (Ollama, llama.cpp, LocalAI, etc.)
|
||||||
- Other compatible AI services
|
- Any other compatible service
|
||||||
|
|
||||||
Configure your preferred service in the Settings page.
|
Llama.cpp-specific features like per-token timings are automatically detected.
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### API Connection Issues
|
|
||||||
|
|
||||||
If you see authentication errors, verify your API endpoint URL is correct and accessible.
|
|
||||||
|
|
||||||
### Port Already in Use
|
|
||||||
|
|
||||||
Change the port using the `--port` flag if port 8080 is unavailable.
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
@@ -2,69 +2,93 @@
|
|||||||
|
|
||||||
## Stack
|
## Stack
|
||||||
|
|
||||||
- **Go 1.25.5**
|
- **Go 1.25.5** (`reichard.io/aethera`)
|
||||||
- **cobra** (CLI framework)
|
- **cobra** (CLI framework)
|
||||||
- **logrus** (structured logging)
|
- **logrus** (structured logging)
|
||||||
- **openai-go/v3** (OpenAI API client)
|
- **openai-go/v3** (OpenAI API client)
|
||||||
|
- **jsonschema-go** (structured output schema generation)
|
||||||
|
- **testify** (testing assertions)
|
||||||
- **golangci-lint** (linting)
|
- **golangci-lint** (linting)
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
go build -o ./dist/aethera ./cmd
|
go build -o ./dist/aethera ./cmd # Build binary
|
||||||
golangci-lint run
|
golangci-lint run # Lint
|
||||||
go test ./...
|
go test ./... # Run all tests
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
Frontend assets are embedded at compile time via `web/embed.go` (`//go:embed static/*`). The `make frontend` target copies built frontend output into `backend/web/static/` before the Go build.
|
||||||
|
|
||||||
|
### Directory Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
cmd/ # CLI entry (main.go, config.go)
|
||||||
|
internal/
|
||||||
|
api/ # HTTP handlers, request/response types, streaming generation
|
||||||
|
client/ # OpenAI-compatible API client (chat, images, structured output)
|
||||||
|
server/ # HTTP server setup, routing, logging middleware
|
||||||
|
store/ # Store interface + FileStore (JSON files) & InMemoryStore
|
||||||
|
storage/ # Standalone file utilities (e.g. ListImages)
|
||||||
|
types/ # Shared domain types (MessageStats)
|
||||||
|
pkg/
|
||||||
|
ptr/ # Generic pointer helpers (Of, DerefOrZero)
|
||||||
|
slices/ # Generic slice helpers (Map, First, Last, FindFirst)
|
||||||
|
values/ # Generic value helpers (FirstNonZero, CountNonZero)
|
||||||
|
web/ # Embedded static assets (embed.go)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key Patterns
|
||||||
|
|
||||||
|
- **Store interface** (`internal/store/interface.go`): abstraction over settings + chat persistence. Two implementations:
|
||||||
|
- `FileStore` — JSON files on disk (settings.json + `chats/*.json`). Used in production.
|
||||||
|
- `InMemoryStore` — map-based, thread-safe with `sync.RWMutex`. Used in tests.
|
||||||
|
- **DI via constructors**: `api.New(store, dataDir, logger)`, `client.NewClient(baseURL)`, `store.NewFileStore(path)`
|
||||||
|
- **Streaming**: `generationManager` coordinates concurrent chat completions. One active generation per chat. Subscribers receive `MessageChunk` updates via channels over NDJSON (`application/x-ndjson`).
|
||||||
|
- **Client lazy init**: `api.getClient()` creates the OpenAI client on first use from stored settings, invalidated on settings change.
|
||||||
|
- **Env vars**: all prefixed `AETHERA_` (e.g. `AETHERA_PORT`, `AETHERA_DATA_DIR`). CLI flags override env.
|
||||||
|
|
||||||
|
### API Routes
|
||||||
|
|
||||||
|
| Method | Path | Handler |
|
||||||
|
|----------|-------------------------------|--------------------|
|
||||||
|
| GET | `/api/settings` | GetSettings |
|
||||||
|
| POST | `/api/settings` | PostSettings |
|
||||||
|
| GET | `/api/models` | GetModels |
|
||||||
|
| GET | `/api/images` | GetImages |
|
||||||
|
| POST | `/api/images` | PostImage |
|
||||||
|
| DELETE | `/api/images/{filename}` | DeleteImage |
|
||||||
|
| GET | `/api/chats` | GetChats |
|
||||||
|
| POST | `/api/chats` | PostChat |
|
||||||
|
| GET | `/api/chats/{chatId}` | GetChat |
|
||||||
|
| POST | `/api/chats/{chatId}` | PostChatMessage |
|
||||||
|
| DELETE | `/api/chats/{chatId}` | DeleteChat |
|
||||||
|
| GET | `/api/chats/{chatId}/stream` | GetChatStream |
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
All Go code is tested using the [testify](https://github.com/stretchr/testify) framework with comprehensive test coverage:
|
- Use `testify` (`require` for fatal, `assert` for non-fatal)
|
||||||
|
- Test both success and error paths
|
||||||
- **Unit Tests**: Each function and method is thoroughly tested including edge cases
|
- Table-driven tests where appropriate
|
||||||
- **Integration Tests**: Store implementations are tested end-to-end
|
- File-based storage tests use temp directories
|
||||||
- **Error Handling**: All error conditions are explicitly tested
|
- Concurrency safety is verified
|
||||||
- **Concurrency**: Thread-safety of concurrent operations is verified
|
|
||||||
- **File Operations**: File-based storage tests use temporary directories
|
|
||||||
|
|
||||||
Tests follow these conventions:
|
|
||||||
- Use `require.NoError(t, err)` for fatal errors that break test flow
|
|
||||||
- Use `assert.NoError(t, err)` for non-fatal assertions
|
|
||||||
- Test both success and error paths for all methods
|
|
||||||
- Use table-driven tests where appropriate
|
|
||||||
- All tests are run with `go test ./...` or `go test -v ./...`
|
|
||||||
|
|
||||||
## Non-Negotiables
|
## Non-Negotiables
|
||||||
|
|
||||||
- ❌ No unhandled errors - always check `err`
|
- ❌ No unhandled errors — always check `err`
|
||||||
- ❌ No ignored linter warnings
|
- ❌ No ignored linter warnings
|
||||||
- ❌ No sensitive data in logs
|
- ❌ No sensitive data in logs
|
||||||
- ❌ No hardcoded paths - use `path.Join`
|
- ❌ No hardcoded paths — use `path.Join`
|
||||||
- ❌ No unsafe file access - use `filepath.Base`
|
- ❌ No unsafe file access — use `filepath.Base`
|
||||||
- ❌ Don't skip tests or linting
|
- ❌ Don't skip tests or linting
|
||||||
|
|
||||||
## Code Style
|
## Code Style
|
||||||
|
|
||||||
- tab indentation, PascalCase exports, camelCase internal
|
- Tab indentation, PascalCase exports, camelCase internal
|
||||||
- Error wrapping with context: `fmt.Errorf("...: %w", err)`
|
- Error wrapping with context: `fmt.Errorf("...: %w", err)`
|
||||||
- Custom error types for domain errors (e.g., `ChatNotFoundError`)
|
- Sentinel errors for domain cases (`ErrChatNotFound`, `ErrNilChatID`)
|
||||||
- Struct tags for JSON with `omitempty`
|
- Struct tags for JSON with `omitempty`
|
||||||
- Log with context: `log.WithField("key", val)`
|
- Log with context: `log.WithField("key", val)`
|
||||||
- Clean up resources with `defer`
|
- Clean up resources with `defer`
|
||||||
|
|
||||||
## Key Patterns
|
|
||||||
|
|
||||||
- **Interfaces**: `Store` interface for swappable backends
|
|
||||||
- **DI**: Dependencies through constructors (`New*` functions)
|
|
||||||
- **HTTP**: Handlers receive `store.Store`, validate inputs, return proper status codes
|
|
||||||
- **Streaming**: Use `FlushWriter` for SSE/text streams
|
|
||||||
- **Storage**: JSON file-based (`FileStore` implementation) with in-memory alternative (`InMemoryStore`)
|
|
||||||
|
|
||||||
## What Goes Where
|
|
||||||
|
|
||||||
- CLI entry: `cmd/` (main.go, config.go)
|
|
||||||
- HTTP handlers: `internal/api/`
|
|
||||||
- OpenAI client: `internal/client/`
|
|
||||||
- Server setup: `internal/server/`
|
|
||||||
- Storage interface & impl: `internal/store/`
|
|
||||||
- Storage utilities: `internal/storage/`
|
|
||||||
- Utilities: `pkg/` (ptr, slices)
|
|
||||||
|
|||||||
@@ -2,25 +2,61 @@
|
|||||||
|
|
||||||
## Stack
|
## Stack
|
||||||
|
|
||||||
- **Tailwind CSS 4** (no config file, just `style.css`)
|
- **Bun** (bundler + package manager — no npm)
|
||||||
- **Bun only** (no npm commands)
|
- **TypeScript** (strict mode, ES2020 target)
|
||||||
- **TypeScript strict mode**
|
- **Alpine.js** (reactivity, bundled via `main.ts` — not CDN)
|
||||||
- **Alpine.js** (bundled in `main.js`, not via CDN)
|
- **Tailwind CSS 4** (no config file, just `styles.css` with `@import`)
|
||||||
|
- **marked + marked-highlight + highlight.js** (Markdown rendering)
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
bun run build
|
bun run build # Production build → public/dist/
|
||||||
bun run lint
|
bun run dev # Watch mode (JS + CSS)
|
||||||
|
bun run lint # ESLint
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### Directory Layout
|
||||||
|
|
||||||
|
```
|
||||||
|
src/
|
||||||
|
main.ts # Entry point — imports components, starts Alpine
|
||||||
|
client.ts # All API calls (fetch-based, centralized)
|
||||||
|
types/index.ts # Shared TypeScript interfaces
|
||||||
|
utils.ts # Shared utility functions
|
||||||
|
theme.ts # Theme toggle logic
|
||||||
|
components/
|
||||||
|
chatManager.ts # Chat UI — message list, streaming, Markdown
|
||||||
|
imageManager.ts # Image generation/gallery UI
|
||||||
|
settingsManager.ts # Settings form UI
|
||||||
|
themeManager.ts # Theme Alpine component
|
||||||
|
navigationManager.ts # Page navigation Alpine component
|
||||||
|
public/
|
||||||
|
index.html # SPA shell
|
||||||
|
pages/
|
||||||
|
chats.html # Chat page template
|
||||||
|
images.html # Images page template
|
||||||
|
settings.html # Settings page template
|
||||||
|
styles.css # Tailwind entry point
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key Patterns
|
||||||
|
|
||||||
|
- **Alpine.js components**: each `*Manager.ts` registers an `Alpine.data(...)` component. State lives in Alpine's reactive scope + localStorage for persistence.
|
||||||
|
- **API client**: all backend calls go through `src/client.ts`. Streaming uses NDJSON via `ReadableStream` reader.
|
||||||
|
- **Build pipeline**: Bun bundles `src/main.ts` → `public/dist/main.js`; Tailwind CLI compiles `styles.css` → `public/dist/styles.css`. Output is copied to `backend/web/static/` by the root `Makefile`.
|
||||||
|
- **No SPA router**: navigation is handled by Alpine component visibility toggling.
|
||||||
|
|
||||||
## Non-Negotiables
|
## Non-Negotiables
|
||||||
|
|
||||||
- ❌ No `any` type - use `unknown` and narrow it
|
- ❌ No `any` type — use `unknown` and narrow
|
||||||
- ❌ No `as` type assertions
|
- ❌ No `as` type assertions
|
||||||
- ❌ No `@ts-ignore` or `@ts-expect-error`
|
- ❌ No `@ts-ignore` or `@ts-expect-error`
|
||||||
- ❌ Fix all TypeScript and ESLint errors - don't ignore them
|
- ❌ Fix all TypeScript and ESLint errors — don't ignore them
|
||||||
- ❌ No Alpine.js via CDN (it's bundled)
|
- ❌ No Alpine.js via CDN (it's bundled)
|
||||||
|
- ❌ Don't commit `public/dist/`
|
||||||
|
|
||||||
## Code Style
|
## Code Style
|
||||||
|
|
||||||
@@ -29,17 +65,4 @@ bun run lint
|
|||||||
- PascalCase for types/interfaces
|
- PascalCase for types/interfaces
|
||||||
- UPPER_SNAKE_CASE for constants
|
- UPPER_SNAKE_CASE for constants
|
||||||
- Explicit error handling with try/catch
|
- Explicit error handling with try/catch
|
||||||
- User-friendly error messages in UI
|
- User-friendly error messages in UI (not just console.log)
|
||||||
|
|
||||||
## Key Patterns
|
|
||||||
|
|
||||||
- **DRY**: Extract repeated code into shared functions
|
|
||||||
- **API calls**: Centralize in `src/client.ts`
|
|
||||||
- **State**: Use Alpine.js reactivity + localStorage for persistence
|
|
||||||
- **Errors**: Show in UI, don't just console.log
|
|
||||||
|
|
||||||
## What Goes Where
|
|
||||||
|
|
||||||
- Code: `src/`
|
|
||||||
- Styles: Tailwind classes in HTML + `style.css`
|
|
||||||
- Build output: `public/dist/` (don't commit this)
|
|
||||||
|
|||||||
Reference in New Issue
Block a user