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:
@@ -2,69 +2,93 @@
|
||||
|
||||
## Stack
|
||||
|
||||
- **Go 1.25.5**
|
||||
- **Go 1.25.5** (`reichard.io/aethera`)
|
||||
- **cobra** (CLI framework)
|
||||
- **logrus** (structured logging)
|
||||
- **openai-go/v3** (OpenAI API client)
|
||||
- **jsonschema-go** (structured output schema generation)
|
||||
- **testify** (testing assertions)
|
||||
- **golangci-lint** (linting)
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
go build -o ./dist/aethera ./cmd
|
||||
golangci-lint run
|
||||
go test ./...
|
||||
go build -o ./dist/aethera ./cmd # Build binary
|
||||
golangci-lint run # Lint
|
||||
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
|
||||
|
||||
All Go code is tested using the [testify](https://github.com/stretchr/testify) framework with comprehensive test coverage:
|
||||
|
||||
- **Unit Tests**: Each function and method is thoroughly tested including edge cases
|
||||
- **Integration Tests**: Store implementations are tested end-to-end
|
||||
- **Error Handling**: All error conditions are explicitly tested
|
||||
- **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 ./...`
|
||||
- Use `testify` (`require` for fatal, `assert` for non-fatal)
|
||||
- Test both success and error paths
|
||||
- Table-driven tests where appropriate
|
||||
- File-based storage tests use temp directories
|
||||
- Concurrency safety is verified
|
||||
|
||||
## Non-Negotiables
|
||||
|
||||
- ❌ No unhandled errors - always check `err`
|
||||
- ❌ No unhandled errors — always check `err`
|
||||
- ❌ No ignored linter warnings
|
||||
- ❌ No sensitive data in logs
|
||||
- ❌ No hardcoded paths - use `path.Join`
|
||||
- ❌ No unsafe file access - use `filepath.Base`
|
||||
- ❌ No hardcoded paths — use `path.Join`
|
||||
- ❌ No unsafe file access — use `filepath.Base`
|
||||
- ❌ Don't skip tests or linting
|
||||
|
||||
## Code Style
|
||||
|
||||
- tab indentation, PascalCase exports, camelCase internal
|
||||
- Tab indentation, PascalCase exports, camelCase internal
|
||||
- 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`
|
||||
- Log with context: `log.WithField("key", val)`
|
||||
- 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)
|
||||
|
||||
Reference in New Issue
Block a user