Metadata-Version: 2.4
Name: lmcore
Version: 0.1.1
Summary: Personal core toolkit for AI projects — config loading, LLM client factory, and logging.
Author-email: Luigi Medrano <luigimedrano03@gmail.com>
License: MIT
Project-URL: Homepage, https://github.com/lm45562/lmcore
Project-URL: Repository, https://github.com/lm45562/lmcore
Keywords: ai,llm,openai,anthropic,ollama,config,logging
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: python-dotenv>=1.0.0
Requires-Dist: pyyaml>=6.0
Provides-Extra: ollama
Requires-Dist: ollama>=0.2.0; extra == "ollama"
Provides-Extra: openai
Requires-Dist: openai>=1.0.0; extra == "openai"
Provides-Extra: anthropic
Requires-Dist: anthropic>=0.25.0; extra == "anthropic"
Provides-Extra: all
Requires-Dist: ollama>=0.2.0; extra == "all"
Requires-Dist: openai>=1.0.0; extra == "all"
Requires-Dist: anthropic>=0.25.0; extra == "all"

# lmcore

Personal core toolkit for AI projects. Install once, call with inputs, never edit the package.

Covers three things every project needs:
- **Config** — load and validate `.env` secrets
- **LLM** — build AI clients from a `models.yaml` file
- **Logging** — colored, consistent logging across all modules

---

## Install

```bash
pip install lmcore
```

With specific provider dependencies:

```bash
pip install lmcore[openai]
pip install lmcore[anthropic]
pip install lmcore[ollama]
pip install lmcore[all]       # all three providers
```

---

## Config

Call `load_config` once at startup. Pass the keys your project needs — the package handles loading from `.env`, validating, and returning a plain dict.

```python
from lmcore import load_config

cfg = load_config(
    required=["OPENAI_API_KEY", "CHROMA_API_KEY"],
    optional={"ACTIVE_ARCH": "arch_1", "LOG_LEVEL": "INFO"}
)
```

- `required` — must exist in `.env`, raises `EnvironmentError` if any are missing
- `optional` — uses the default value if not set in `.env`

---

## LLM

### models.yaml

Lives in your **project root**. Define only what your project uses — `chat` and `embed` are both optional. Each section is a list so you can define multiple clients and reference them by index.

```yaml
arch_1:
  chat:
    - provider: openai
      model: gpt-4o
      api_key_env: OPENAI_API_KEY
    - provider: anthropic
      model: claude-sonnet-4-6
      api_key_env: ANTHROPIC_API_KEY
  embed:
    - provider: ollama
      model: nomic-embed-text
      base_url_env: OLLAMA_URL

arch_2:
  chat:
    - provider: anthropic
      model: claude-opus-4-8
      api_key_env: ANTHROPIC_API_KEY
```

**Supported fields per client:**

| Field | Required | Description |
|---|---|---|
| `provider` | yes | `ollama`, `openai`, or `anthropic` |
| `model` | yes | model name string |
| `api_key_env` | when needed | env var name that holds the API key |
| `base_url` | no | override default endpoint (e.g. local Ollama) |
| `base_url_env` | no | env var name that holds the base URL (alternative to `base_url`) |

### Getting clients

```python
from lmcore import get_chat_client, get_embed_client

# index defaults to 0 — covers most single-client projects
chat  = get_chat_client("models.yaml", arch="arch_1")
embed = get_embed_client("models.yaml", arch="arch_1")

# specify index for multiple clients
openai_client    = get_chat_client("models.yaml", arch="arch_1", index=0)
anthropic_client = get_chat_client("models.yaml", arch="arch_1", index=1)
```

### Adding a custom provider

If you need a provider not built into the package, register it in your project:

```python
from lmcore import register_provider

def _build_groq(cfg):
    from groq import Groq
    return Groq(api_key=cfg["api_key"])

register_provider("groq", _build_groq)
```

Once registered, `groq` works as a `provider:` value in `models.yaml` like any built-in.

---

## Logging

```python
from lmcore import configure_logging, get_logger

configure_logging()          # call once at app startup
configure_logging("DEBUG")   # optional level override

logger = get_logger(__name__)

logger.info("Starting up")
logger.warning("Something looks off")
logger.error("Connection failed")
```

Color scheme:

| Color | Level |
|---|---|
| Cyan | INFO |
| Yellow | WARNING |
| Red | ERROR / CRITICAL |
| Dim | DEBUG |

---

## Full Example

**Project layout:**
```
my-project/
├── .env
├── models.yaml
└── app/
    └── core/
        ├── config.py
        └── llm.py
```

**`.env`:**
```
OPENAI_API_KEY=sk-...
ACTIVE_ARCH=arch_1
```

**`models.yaml`:**
```yaml
arch_1:
  chat:
    - provider: openai
      model: gpt-4o
      api_key_env: OPENAI_API_KEY
    - provider: ollama
      model: llama3
      base_url_env: OLLAMA_URL
```

**`app/core/config.py`:**
```python
from lmcore import load_config, configure_logging

configure_logging()

cfg = load_config(
    required=["OPENAI_API_KEY"],
    optional={"ACTIVE_ARCH": "arch_1"}
)
```

**`app/core/llm.py`:**
```python
from lmcore import get_chat_client
from app.core.config import cfg

chat = get_chat_client("models.yaml", arch=cfg["ACTIVE_ARCH"])
```

**Anywhere in the project:**
```python
from lmcore import get_logger
from app.core.llm import chat

logger = get_logger(__name__)

response = chat.chat(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Summarize this document."}]
)
logger.info(f"Done: {response.choices[0].message.content}")
```

---

## Built-in Providers

| Provider | `provider:` value | Install extra |
|---|---|---|
| Ollama | `ollama` | `pip install lmcore[ollama]` |
| OpenAI | `openai` | `pip install lmcore[openai]` |
| Anthropic | `anthropic` | `pip install lmcore[anthropic]` |
