Metadata-Version: 2.4
Name: nover
Version: 2.0.0
Summary: Nover - typed Python SDK + CLI for self-hosted, multi-provider AI gateways (chat, tools, images, TTS, STT, embeddings, web)
Project-URL: Homepage, https://github.com/ChristopherDond/nover
Project-URL: Documentation, https://github.com/ChristopherDond/nover
Project-URL: Source, https://github.com/ChristopherDond/nover
License: MIT
License-File: LICENSE
Keywords: 9router,agent,ai,embeddings,gateway,llm,nover,openai,routing,stt,tts
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Requires-Dist: httpx>=0.25
Requires-Dist: typer>=0.9
Provides-Extra: dev
Requires-Dist: hatchling; extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Requires-Dist: respx>=0.20; extra == 'dev'
Requires-Dist: textual>=0.55; extra == 'dev'
Provides-Extra: interactive
Requires-Dist: textual>=0.80; extra == 'interactive'
Description-Content-Type: text/markdown

<p align="center">
  <img src="https://img.shields.io/pypi/v/nover?color=4a90d9" alt="PyPI" />
  <img src="https://img.shields.io/pypi/pyversions/nover" alt="Python versions" />
  <img src="https://img.shields.io/github/license/ChristopherDond/nover" alt="License" />
  <img src="https://img.shields.io/pypi/dm/nover" alt="Downloads" />
</p>

<div align="right">

[Português (PT-BR)](README-pt-br.md)

</div>

# Nover

**Nover** is a typed Python SDK + CLI for **self-hosted, multi-provider AI gateways** (9Router-compatible). One local endpoint routes to **Gemini, NVIDIA, OpenRouter, Groq, Ollama, Tavily and Exa** with automatic fallback — and Nover lets you talk to all of it from Python or the terminal: chat (streaming + tool calling), structured JSON output, images, TTS, STT, embeddings and web search/fetch.

```console
$ pip install nover
```

---

## ✨ What you can do

| Capability | SDK method | CLI |
|---|---|---|
| 💬 Chat (streaming) | `client.chat()` / `chat_stream()` | `nover chat "..." --stream` |
| 🛠️ Tool / function calling | `chat(..., tools=...)`, `chat_with_tools()` | via code |
| 📐 Structured output (JSON) | `chat(..., response_format=...)` | `nover chat --json` |
| 🔄 Async | `NoverAsync` | — |
| 🌀 Embeddings | `client.embeddings()` | `nover embed` |
| 🖼️ Image generation | `client.image()` | `nover image` |
| 🔊 Text-to-speech | `client.tts()` | `nover tts` |
| 🎙️ Speech-to-text | `client.stt()` | `nover stt` |
| 🔎 Web search | `client.web_search()` | `nover web search` |
| 📄 Fetch URL → markdown | `client.web_fetch()` | `nover web fetch` |
| 🖥️ Interactive chat (TUI) | — | `nover interactive` |
| 🔌 OpenAI-compatible | `from nover import OpenAICompat` | — |

---

## 🚀 Quickstart

### 30 seconds to first chat

```console
$ pip install nover
$ nover chat "Hello, world!" --stream
```

### Chat & tools

```python
from nover import Nover

with Nover() as client:
    reply = client.chat("Explain a monad in one sentence")
    print(reply.text)
```

Tool calling:

```python
from nover import Nover, tool

weather = tool("get_weather", "Get weather for a city",
               {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]})

with Nover() as c:
    reply = c.chat("What's the weather in Paris?",
                   tools=[weather],
                   tool_choice="auto")
    print(reply.tool_calls)   # None, or [{name, arguments}]
```

Auto-execute tools with an agent-style loop:

```python
handlers = {"get_weather": lambda args: {"temp": 25}}
reply = c.chat_with_tools("Weather in Paris?", tools=[weather], tool_handler=handlers)
print(reply.text)
```

Structured output:

```python
reply = c.chat("Return JSON: {\"name\": \"Ada\", \"age\": 36}",
               response_format={"type": "json_object"})
print(reply.text)   # clean JSON (code fences auto-stripped)
```

### Async

```python
from nover import NoverAsync
import asyncio

async def main():
    async with NoverAsync() as c:
        return await c.chat("Hi")

print(asyncio.run(main()).text)
```

### Stay with the `openai` SDK

`OpenAICompat` is a drop-in: code written against `openai` keeps working.

```python
from nover import OpenAICompat

client = OpenAICompat()
r = client.chat.completions.create(
    model="Code",
    messages=[{"role": "user", "content": "Say NOVER"}],
    stream=True,
)
for chunk in r:
    print(chunk["choices"][0]["delta"].get("content", ""), end="")
```

### Images, TTS, STT, embeddings, web — all in one

```python
img = client.image("a watercolor of mountains")
open("mountains.png", "wb").write(img.content)

audio = client.tts("Olá, mundo", voice="pt-BR-FernandaNeural")
open("speech.wav", "wb").write(audio)

text = client.stt(("rec.wav", open("rec.wav", "rb").read()))
print(text.text)

vec = client.embeddings("RAG-ready footnote")[0]

res = client.web_search("9Router open source")
for r in res.results: print(r.title, "-", r.url)

page = client.web_fetch("https://example.com")
print(page.content.text)
```

---

## 🖥️ CLI

```
nover                          # help
nover version
nover health                   # gateway status
nover models --kind chat       # list chat models
nover chat "Hello" --stream    # chat with streaming
nover interactive              # interactive TUI chat
nover embed "text"
nover image "a red fox" --out fox.png
nover tts "Hello" --voice pt-BR-FernandaNeural
nover stt recording.wav
nover web search "9Router"
nover web fetch linkedin.com/p
nover config
```

`ninerouter` aliases the same `nover` CLI for compatibility.

---

## 🔧 Configuration

Resolution order: **CLI flags > env vars > defaults.**

| Setting | Env var | Default |
|---|---|---|
| Base URL | `NINEROUTER_URL` | `http://localhost:20128` |
| API key | `NINEROUTER_KEY` | *(optional)* |

```bash
export NINEROUTER_URL="http://localhost:20128"
export NINEROUTER_KEY="sk-..."     # optional if auth disabled
```

---

## ☁️ Into the ecosystem

- **OpenAI-compatible**: `OpenAICompat` swaps into any stack that expects `openai`.
- Designed to sit behind **LangChain/LiteLLM** style routers and orchestrators.

---

## 🔒 Privacy & Security

`nover` is a *thin HTTP client*. It stores no provider keys, sends your prompts
nowhere except the gateway you configure, and has **no telemetry**.

- Provider keys live in *your* gateway, on *your* machine/VM — not in this library.
- Only tight runtime deps (`httpx`, `typer`), CI-tested across Python 3.9–3.13.
- A CI test scans the repo for committed credentials.

See `SECURITY.md` for details.

---

## 📦 Install & develop

```bash
pip install -e ".[dev]"
pytest                       # unit + live tests (live needs gateway)
pip install nover[interactive]  # for the TUI
```

Requires Python 3.9+.

---

## 🇧🇷 Português

**SDK Python + CLI para gateways de IA multi-provedor self-hosted.**

Um único pacote que conversa com um gateway compatível com a API da OpenAI que
roteia **Gemini, NVIDIA, OpenRouter, Groq, Ollama, Tavily e Exa** — com chat,
tools, saída estruturada em JSON, imagens, TTS, STT, embeddings e busca web.

```bash
pip install "nover"
nover chat "Olá, mundo" --stream
nover interactive
```

Config, quickstart e CLI são idênticos à seção em inglês acima.

---

## 📄 License

MIT © ChristopherDond

---

[Português (PT-BR)](README-pt-br.md) · <a href="#nover">⬆ back to top</a>
