Metadata-Version: 2.4
Name: vikky
Version: 0.1.0
Summary: Python SDK for Vikky Platform: chat, vision, speech, embeddings, images and more with one key.
Keywords: vikky,vikkyverse,ai,llm,sdk,api
Author: VSP AI & Robotics
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Typing :: Typed
Requires-Dist: openai>=1.55.3
Requires-Python: >=3.9
Project-URL: Homepage, https://vikkyverse.com
Project-URL: Documentation, https://vikkyverse.com/docs
Project-URL: Get an API key, https://vikkyverse.com/platform
Description-Content-Type: text/markdown

# vikky

Python client for **Vikky**, VSP's AI API gateway. Vikky speaks the OpenAI API,
so this package is a thin wrapper over the official `openai` package: anything
in the OpenAI Python docs works here too.

## Install

```bash
pip install vikky
```

In Colab or Jupyter, use `%pip install vikky`.

## Get a key

Sign in to the Vikky console at https://vikkyverse.com/platform and create an API key. Keep it secret: anyone with
the key spends your quota.

## Set VIKKY_API_KEY

On your laptop:

```bash
export VIKKY_API_KEY="your-key"
```

In Google Colab: click the key icon in the left sidebar, add a secret named
`VIKKY_API_KEY`, turn on "Notebook access", then run:

```python
import os
from google.colab import userdata

os.environ["VIKKY_API_KEY"] = userdata.get("VIKKY_API_KEY")
```

Never paste the key into a notebook cell you might share.

## First call (JSON out)

```python
import json
from vikky import Vikky

client = Vikky()  # reads VIKKY_API_KEY

resp = client.chat.completions.create(
    model="vikky-chat",
    messages=[
        {"role": "system", "content": "Reply in JSON."},
        {"role": "user", "content": 'List 3 planets as {"planets": [...]}'},
    ],
    response_format={"type": "json_object"},
)
data = json.loads(resp.choices[0].message.content)
print(data["planets"])
```

JSON mode works best when your messages say "JSON" and show the shape you want.

## Streaming

```python
stream = client.chat.completions.create(
    model="vikky-chat",
    messages=[{"role": "user", "content": "Explain PID control in 3 lines."}],
    stream=True,
)
for chunk in stream:
    if chunk.choices:
        print(chunk.choices[0].delta.content or "", end="", flush=True)
```

## Models

| Model | What it does | Call it with |
|---|---|---|
| `vikky-chat` | chat, JSON output, tool calling | `chat.completions.create`, or `responses.create` |
| `vikky-vision` | chat that also takes images | `chat.completions.create` with an `image_url` part |
| `vikky-embed` | embeddings | `embeddings.create` |
| `vikky-transcribe` | audio to text | `audio.transcriptions.create` |
| `vikky-speech` | text to audio | `audio.speech.create` |
| `vikky-image` | image generation and editing | `images.generate`, `images.edit` |
| `vikky-video` | video generation, async | `videos.create_and_poll` |
| `vikky-rerank` | rank documents against a query | `rerank` |
| `vikky-ocr` | text out of a document | `ocr` |
| `vikky-moderate` | content moderation | `moderations.create` |

Every row except the last two is a method the `openai` package already has, so
the OpenAI Python docs apply unchanged. `rerank` and `ocr` are not OpenAI
routes: they are the only two methods this package adds, and they return a
plain dict instead of a typed object.

## Tool calling

```python
resp = client.chat.completions.create(
    model="vikky-chat",
    messages=[{"role": "user", "content": "Weather in Hyderabad?"}],
    tools=[{"type": "function", "function": {
        "name": "get_weather",
        "parameters": {"type": "object", "properties": {"city": {"type": "string"}}},
    }}],
)
call = resp.choices[0].message.tool_calls[0]
print(call.function.name, call.function.arguments)
```

## Responses API

```python
resp = client.responses.create(model="vikky-chat", input="Say hello.")
print(resp.output_text)
```

## Vision

```python
resp = client.chat.completions.create(
    model="vikky-vision",
    messages=[{"role": "user", "content": [
        {"type": "text", "text": "What is in this picture?"},
        {"type": "image_url", "image_url": {"url": "https://example.com/arm.jpg"}},
    ]}],
)
print(resp.choices[0].message.content)
```

For a local image, pass a data URL as the `url`:

```python
import base64, pathlib

raw = base64.b64encode(pathlib.Path("arm.png").read_bytes()).decode()
url = f"data:image/png;base64,{raw}"
```

## Embeddings

```python
resp = client.embeddings.create(model="vikky-embed", input=["robot arm", "pizza"])
arm, pizza = (d.embedding for d in resp.data)
print(len(arm), len(pizza))
```

Pass a list to embed a batch in one call. Results come back in the order you
sent them, and `d.index` tells you which input each vector belongs to.

## Audio to text

```python
with open("meeting.m4a", "rb") as f:
    resp = client.audio.transcriptions.create(model="vikky-transcribe", file=f)
print(resp.text)
```

## Text to audio

```python
resp = client.audio.speech.create(
    model="vikky-speech",
    voice="alloy",
    input="The arm is homed and ready.",
)
resp.write_to_file("ready.mp3")
```

## Images

```python
resp = client.images.generate(model="vikky-image", prompt="a blue robot arm on a bench", n=1)
item = resp.data[0]
print(item.url or "returned as base64")  # either one, see "Saving generated media"

with open("arm.png", "rb") as f:
    edited = client.images.edit(model="vikky-image", image=f, prompt="make the bench wooden")
```

## Video

Video generation takes minutes, so it is a job, not a call. Submit it, wait for
it, then download it. Keep the id `create` gave you and download with that one.

```python
job = client.videos.create(model="vikky-video", prompt="a robot arm picking up a cube")
done = client.videos.poll(job.id, poll_interval_ms=5000)
assert done.status == "completed", done.error
client.videos.download_content(job.id).write_to_file("clip.mp4")
```

`client.videos.retrieve(job.id)` is the single-shot version of `poll`, if you
want to show `video.progress` in your own loop. Do not use
`videos.create_and_poll`: it only returns the polled job, and Vikky's polled id
cannot be downloaded from.

## Saving generated media

An image comes back as either a URL or base64, depending on what you asked
for. Audio and video come back as a binary response with `write_to_file`.

```python
import base64, pathlib, urllib.request

item = client.images.generate(model="vikky-image", prompt="a blue cube").data[0]
if item.b64_json:
    pathlib.Path("cube.png").write_bytes(base64.b64decode(item.b64_json))
else:
    with urllib.request.urlopen(item.url) as r:
        pathlib.Path("cube.png").write_bytes(r.read())
```

Ask for `response_format="b64_json"` and you never have to fetch a URL at all.

**A generated file's URL is temporary.** Download it in the same run that
created it. Do not store the URL in a database or a notebook output and expect
it to still work tomorrow.

## Rerank

```python
resp = client.rerank(
    query="how do I reset the arm?",
    documents=[
        "Press the red button to reset the arm.",
        "Our office is in Hyderabad.",
    ],
    top_n=1,
)
for r in resp["results"]:
    print(r["index"], r["relevance_score"])
```

Results come back best first. `r["index"]` points back into the `documents`
list you sent.

## OCR

```python
resp = client.ocr(document={"type": "document_url", "document_url": "https://example.com/invoice.pdf"})
for page in resp["pages"]:
    print(page["markdown"])
```

For an image instead of a PDF, send
`{"type": "image_url", "image_url": "https://..."}`. A data URL works too.

## Moderation

```python
resp = client.moderations.create(model="vikky-moderate", input="Some user text.")
result = resp.results[0]
print(result.flagged, [name for name, hit in result.categories if hit])
```

## Async

```python
import asyncio
from vikky import AsyncVikky

async def main():
    client = AsyncVikky()
    resp = await client.chat.completions.create(
        model="vikky-chat",
        messages=[{"role": "user", "content": "Say hello."}],
    )
    print(resp.choices[0].message.content)

asyncio.run(main())  # in Colab or Jupyter, use: await main()
```

Every model above works on `AsyncVikky`, `rerank` and `ocr` included: same
arguments, awaited.

## Environment variables

| Variable | Required | Default |
|---|---|---|
| `VIKKY_API_KEY` | yes | none. `OPENAI_API_KEY` is never used. |
| `VIKKY_BASE_URL` | no | `https://api.vikkyverse.com/v1` |

Arguments win over environment: `Vikky(api_key=..., base_url=...)`. Every other
argument (`timeout`, `max_retries`, ...) goes straight to `openai.OpenAI`.
A missing key raises `vikky.VikkyError`.

## If Vikky is down

Write lab code so the model name comes from the environment:

```python
import os
from vikky import Vikky

MODEL = os.environ.get("VIKKY_MODEL", "vikky-chat")
client = Vikky()
resp = client.chat.completions.create(model=MODEL, messages=[...])
```

Then a trainer can set `VIKKY_BASE_URL`, `VIKKY_API_KEY` and `VIKKY_MODEL` to
any other OpenAI-compatible endpoint, and the notebook runs unchanged.

## License

MIT
