# Track B — Create `@bytelyst/ollama-client` Package

> **Repo:** `learning_ai_common_plat`
> **Can start:** Immediately (no dependencies)
> **Estimated time:** ~1 day
> **Roadmap:** `../LOCAL_AI_CONSOLIDATION_ROADMAP.md` — sections 4.1, 4.5, 4.6, and 4.7.

---

## Goal

Create a new `@bytelyst/ollama-client` package that eliminates ~350 lines of duplicated Ollama API code across `learning_ai_local_llms` and `learning_ai_local_memory_gpt`.

---

## Package Structure

```
packages/ollama-client/
├── src/
│   ├── index.ts          # Public exports
│   ├── config.ts          # resolveOllamaUrl(env) — OLLAMA_URL/OLLAMA_HOST + WSL2 gateway detection
│   ├── client.ts          # OllamaClient class: tags, ps, show, pull, load, unload, delete, version
│   ├── stream.ts          # streamChat(), streamGenerate() — AsyncGenerator<OllamaStreamChunk>
│   ├── embed.ts           # getEmbedding()
│   ├── health.ts          # checkHealth() with timeout
│   ├── ndjson.ts          # parseNdjsonStream() — reusable NDJSON async generator (server + client)
│   ├── types.ts           # OllamaModel, OllamaChatMessage, OllamaStreamChunk, OllamaEmbedding
│   └── client-parsers.ts  # consumeSSEStream(), consumeNdjsonStream() — browser-side stream consumers
├── package.json
├── tsconfig.json
└── vitest.config.ts
```

---

## Reference Implementations to Consolidate

Read these files from sibling repos to understand the existing patterns:

1. **`../learning_ai_local_memory_gpt/backend/src/lib/ollama.ts`** (117 lines)
   - `listOllamaModels()` — `GET /api/tags`
   - `checkOllamaHealth()` — `GET /` with timeout
   - `streamChat()` — `POST /api/chat` with NDJSON response parsing
   - `getEmbedding()` — `POST /api/embed`

2. **`../learning_ai_local_llms/dashboard/src/app/lib/ollama-config.ts`** (40 lines)
   - `OLLAMA_URL` env resolution
   - WSL2 gateway detection (reads `/etc/resolv.conf` nameserver)

3. **`../learning_ai_local_llms/dashboard/src/app/api/ollama/route.ts`** (128 lines)
   - `fetchOllama()` helper wrapper
   - Model list, pull, delete, show, load, unload operations

4. **NDJSON stream parsing pattern** (copy-pasted 5+ times across both repos):
   ```ts
   const reader = res.body.getReader();
   const decoder = new TextDecoder();
   let buffer = '';
   while (true) {
     const { done, value } = await reader.read();
     if (done) break;
     buffer += decoder.decode(value, { stream: true });
     const lines = buffer.split('\n');
     buffer = lines.pop() ?? '';
     for (const line of lines) {
       if (!line.trim()) continue;
       try {
         yield JSON.parse(line);
       } catch {
         /* skip */
       }
     }
   }
   ```

---

## Types to Include (section 4.5)

Canonical `OllamaModel` type — superset of both repos' definitions, matching Ollama's actual API response:

| Repo                    | Definition                                                                                                              |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| LocalMemGPT `ollama.ts` | `{ name, size, digest, modified_at, details?: { parameter_size, quantization_level, family } }`                         |
| LLM Lab `types.ts`      | `{ name, model, modified_at, size, digest, details: { families, family, format, parameter_size, quantization_level } }` |

---

## Reusable Utilities to Include (section 4.6)

Extract these from `../learning_ai_local_llms/dashboard/src/app/lib/format.ts` (96 lines):

| Function                  | Scope                           |
| ------------------------- | ------------------------------- |
| `formatBytes()`           | General purpose byte formatting |
| `estimateTokens()`        | LLM token estimation            |
| `getModelContextWindow()` | Model context window lookup     |
| `formatUptime()`          | Uptime formatting               |

Leave LLM-Lab-specific functions (`estimateRam`, `checkMemoryFit`, `getModelBadges`) in LLM Lab.

---

## Client-Side Parsers to Include (section 4.7)

Browser-side stream consumers for Next.js API route clients:

```ts
// Client-side SSE stream consumer
export function consumeSSEStream(
  response: Response,
  onData: (data: Record<string, unknown>) => void,
  onDone: () => void
): Promise<void>;

// Client-side NDJSON stream consumer
export function consumeNdjsonStream<T>(
  response: Response,
  onChunk: (chunk: T) => void
): Promise<void>;
```

---

## Conventions

Follow existing `@bytelyst/*` package patterns:

- ESM (`"type": "module"`), TypeScript, `exports` field in `package.json`
- Extend `../../tsconfig.base.json`
- Vitest for tests with `passWithNoTests: true`
- `peerDependencies` for heavy deps if any
- `workspace:*` for inter-package deps
- Build: `tsc` → `dist/`

---

## Scope Boundary

**DO NOT** migrate consumer repos — that happens in Track E. Only create the package, write tests, and publish to Gitea registry.

---

## Commit

`feat(ollama-client): shared Ollama API client package`
