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
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:
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:
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)
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
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
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
resp = client.responses.create(model="vikky-chat", input="Say hello.")
print(resp.output_text)
Vision
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:
import base64, pathlib
raw = base64.b64encode(pathlib.Path("arm.png").read_bytes()).decode()
url = f"data:image/png;base64,{raw}"
Embeddings
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
with open("meeting.m4a", "rb") as f:
resp = client.audio.transcriptions.create(model="vikky-transcribe", file=f)
print(resp.text)
Text to audio
resp = client.audio.speech.create(
model="vikky-speech",
voice="alloy",
input="The arm is homed and ready.",
)
resp.write_to_file("ready.mp3")
Images
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.
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.
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
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
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
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
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:
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
Release files for vikky 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| vikky-0.1.0.tar.gz | 6.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| vikky-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 13.3 kB
Release files / vikky-0.1.0.tar.gz
| Download URL | vikky-0.1.0.tar.gz |
|---|---|
| Size | 6.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d4ad28041719425a6968ae4231d34bd3b56f2bb6d2110e0c2a97086d4b39b2ec
|
|
BLAKE2b-256 checksum How to use checksums |
6b74b664fc3dda08218a77fd48b5693c058ed12f96c018121c34bb3229dce4fe
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / vikky-0.1.0-py3-none-any.whl
| Download URL | vikky-0.1.0-py3-none-any.whl |
|---|---|
| Size | 7.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b6770947fbd95d55f8647d7cc0073984031cdc25594f1a1614e0817f34e2fdf5
|
|
BLAKE2b-256 checksum How to use checksums |
5252cd4a687e6e20e7caf96675dd9ee03bd12c4425dae585b854d4ce817a5d56
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|