# Phase 6 Summary: LLM Integration — Provider Abstraction and Briefing Generation

## Completed

2026-03-07

## What Was Built

### New Files Created

```
src/unfamiliar/llm/
├── __init__.py              # Public API exports
├── exceptions.py            # Exception hierarchy (5 classes)
├── retry.py                 # Retry utilities with exponential backoff
├── context.py               # Context builder for AnalysisResult → LLM text
├── providers/
│   ├── __init__.py          # Provider package exports
│   └── factory.py           # create_chat_model() factory
└── briefing/
    ├── __init__.py          # Briefing package exports
    ├── schemas.py           # BriefingResponse, ModuleDescription Pydantic models
    ├── prompts.py           # BRIEFING_PROMPT ChatPromptTemplate
    └── generator.py         # generate_briefing() orchestrator
```

### Dependencies Added

- `langchain>=0.3` — For `init_chat_model()` unified factory
- `langchain-community>=0.3` — For `ChatZhipuAI` (GLM provider)

### Public API

```python
from unfamiliar.llm import (
    # Factory
    create_chat_model,

    # Briefing
    generate_briefing,
    BriefingResult,
    BriefingResponse,

    # Exceptions
    LLMError,
    ProviderError,
    AuthenticationError,
    BriefingGenerationError,
    TokenBudgetExceededError,
)
```

## Key Components

### create_chat_model(config)
- Creates LangChain chat models from user configuration
- Supports Anthropic and GLM providers
- Maps `GLM_API_KEY` to `ZHIPUAI_API_KEY` for LangChain compatibility
- Default settings: temperature=0.2, max_tokens=4096, max_retries=2

### with_llm_retry(fn, ...)
- Exponential backoff retry for transient LLM errors
- Retries on: 429, 500, 502, 503, 504, connection errors
- Immediate fail on auth errors (401, 403)
- Formula: `base_delay * 2^(attempt-1) + jitter(0-1)`

### build_briefing_context(analysis, token_budget)
- Transforms AnalysisResult to structured LLM context
- Sections: Overview, Entry Points, Modules, Import Graph, Tests, Security, Key Files
- Respects token budget for file content inclusion
- Truncates large files, lists remaining as signatures

### generate_briefing(analysis, model, token_budget)
- Single LLM call produces structured briefing + module naming
- Returns BriefingResult with briefing content and module descriptions
- Handles module count mismatches by padding with defaults
- Wrapped with retry for transient errors

## Design Decisions

1. **Provider Factory Pattern**: Uses `init_chat_model()` from langchain package for portability across providers

2. **Structured Output**: Uses `.with_structured_output(PydanticModel)` via tool-calling for provider portability

3. **Token Estimation**: Simple `len(content) / 4` heuristic is sufficient for budget enforcement

4. **Long Lines in Prompts**: Intentionally exceed 88 characters for LLM readability; suppressed with noqa comments

5. **Module Padding**: If LLM returns fewer modules than detected, generator pads with default names rather than failing

## Testing Status

- Ruff linting: ✅ Passed
- Mypy type checking: ⚠️ Not available (not installed in environment)
- Python syntax: ✅ Validated via py_compile
- Integration tests: ⏳ Deferred to Phase 8

## Next Phase

Phase 7: LLM Integration — Mission Generation, Validation, and Checkpointing
