qunivex
Official Python SDK for the Qunivex API — run your AI agents, search your knowledge base, and manage your projects from Python.
pip install qunivex
from qunivex import Qunivex
qx = Qunivex(api_key="qvx-live-...")
print(qx.chat("QXA-8MQ7KN2A", "What plans do you offer?").content)
Create a key on the API page in your dashboard. Full reference: qunivex.com/docs/api
model is an agent, not a foundation model
This is the one thing to know before you start. A Qunivex agent is a whole configured pipeline — system prompt, knowledge base, tools, MCP servers, sub-agents, guardrails, workflow — built in your dashboard. Which LLM runs inside it is the agent's own setting, not something the caller picks.
So model takes a QXA- agent id. A QXP- project id also works and resolves to that project's main agent.
| Prefix | Names | Example |
|---|---|---|
QXP- |
Project | QXP-1DM2J8K0 |
QXA- |
Agent (main or sub-agent) | QXA-8MQ7KN2A |
Both are on your project's Overview page.
Setup
qx = Qunivex(api_key="qvx-live-...")
Or set QUNIVEX_API_KEY in the environment and pass nothing — worth preferring, since a key in source is a key in version control.
qx = Qunivex() # reads QUNIVEX_API_KEY
qx = Qunivex(timeout=60, max_retries=4) # tune transport
with Qunivex() as qx: ... # closes the HTTP session
Rate limits and transient server errors are retried automatically with exponential backoff and jitter, honouring Retry-After. A quota 429 is not retried — waiting will not help.
Chat
reply = qx.chat("QXA-8MQ7KN2A", "What plans do you offer?")
reply.content # the text
reply.total_tokens # usage for this turn
reply.blocked # True if a guardrail stopped it
reply.raw # the untouched API payload
Stream it:
for piece in qx.stream("QXA-8MQ7KN2A", "Tell me about Pro"):
print(piece, end="", flush=True)
Multi-turn
The API is stateless — every request carries the full history, exactly like OpenAI's. That makes each call reproducible and leaves the context window under your control.
Manage the transcript yourself:
history = [{"role": "user", "content": "What plans do you offer?"}]
r = qx.chat(AGENT, history[-1]["content"])
history.append({"role": "assistant", "content": r.content})
…or let a Conversation do it:
convo = qx.conversation("QXA-8MQ7KN2A", system="Be concise.")
convo.send("What plans do you offer?")
convo.send("Which suits two people?") # remembers the first turn
convo.messages # the running transcript — mutable, trim it if you like
convo.reset() # start over, keeping the system message
A Conversation also generates one conversation_id and sends it with every turn, so the whole exchange is grouped together in your dashboard's Logs.
Raw OpenAI shape
When you need a field the wrappers do not surface:
resp = qx.completions.create(
model="QXA-8MQ7KN2A",
messages=[{"role": "user", "content": "Hello"}],
temperature=0.2,
)
resp["choices"][0]["message"]["content"]
Semantic search
Retrieval only — no model call, so it is far cheaper and faster than a completion. Ideal for adding search to your own site over content already in Qunivex.
for hit in qx.search("QXP-1DM2J8K0", "refund policy", top_k=3):
print(round(hit.score, 3), hit.source, hit.text[:100])
score is a relevance figure in (0, 1] — higher is better. Pass agent= to search a sub-agent's own knowledge base instead of the project's.
If the embedding provider is down this raises ServiceUnavailableError rather than returning an empty list, so "nothing matched" and "search is broken" never look the same.
Projects
for p in qx.projects.list():
print(p.id, p.name)
project = qx.projects.retrieve("QXP-1DM2J8K0")
project.name # 'Acme Support'
project.main_agent.system_prompt
project.main_agent.model
project.sub_agents # [AgentInfo, ...]
project.knowledge.documents # 12
project.update(
name="Acme Support",
main_agent={"system_prompt": "Be concise and always cite a source.",
"temperature": 0.3},
)
project.chat("What plans do you offer?") # via the main agent
project.search("refund policy")
Editable at the top level: name, description. Under main_agent: name, system_prompt, model, temperature, max_tokens, memory, widget_enabled. Only fields you pass are changed.
Knowledge base
docs = project.documents
docs.upload("handbook.pdf")
docs.add_text("faq.txt", "Q: Do you ship internationally? A: Yes…")
for d in docs.list():
print(d.filename, d.status, d.chunks, d.tokens)
docs.delete(doc_id)
Indexing happens before the response returns, so the Document you get back already carries its final status and chunk count — nothing to poll. Supported: PDF, DOCX, TXT, MD, HTML, JSON, XML, RTF, YAML and other plain-text formats.
Uploads count against your plan's knowledge-base token budget.
Discovery & usage
for m in qx.models():
print(m.id, m.name, m.project_name, m.model_name)
u = qx.usage()
print(f"{u.api_calls_used}/{u.api_calls_limit} calls, {u.api_calls_remaining} left")
Neither counts against your quota.
Errors
from qunivex import (
QunivexError, AuthenticationError, PermissionDeniedError,
NotFoundError, RateLimitError, QuotaExceededError,
)
try:
qx.chat(AGENT, "hello")
except QuotaExceededError:
... # monthly allowance spent — upgrade or wait
except RateLimitError as e:
... # burst limit; e.retry_after has the wait
except AuthenticationError:
... # bad or revoked key
except QunivexError as e:
print(e.status, e.code, e.message)
Every error subclasses QunivexError, so that last clause is a complete catch.
NotFoundError is also what you get for a project this key was scoped away from — the API does not distinguish the two, so key scoping cannot be used to probe what sits behind it.
Keys & security
- Keys look like
qvx-live-plus 28 characters, and the full value is shown once, when you create it. Only a hash is stored, so it cannot be recovered — lose it, revoke it, mint a new one. - A key belongs to your account, and can optionally be narrowed to specific projects (or to none) when you create or edit it.
- A key carries the full rights of your account within its scope, including editing an agent's prompt and deleting documents. Never ship one to a browser, a mobile app, or a public repo. For a chat widget on a website, use the embed snippet on your project's Deployments page — that uses a separate, domain-locked public key designed to be visible.
Requirements
Python 3.8+ and requests. That is the whole dependency list.
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file qunivex-1.0.0.tar.gz.
File metadata
- Download URL: qunivex-1.0.0.tar.gz
- Upload date:
- Size: 19.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
346ac99052920ac11084ceb591ec41d88a40cfc440a79194cbbeeb2312f653cb
|
|
| MD5 |
a60b60e4d9a8410b925614d0fd31a6e0
|
|
| BLAKE2b-256 |
fe4f49a97a794754a803cb4cf41ff24916ef9d5e548a59b282a9a8e937caa716
|
File details
Details for the file qunivex-1.0.0-py3-none-any.whl.
File metadata
- Download URL: qunivex-1.0.0-py3-none-any.whl
- Upload date:
- Size: 19.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.14.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cafe53f4693a8ca22996bad61d931d1ae8f88fbe5eab2995a393f3218ca93bdf
|
|
| MD5 |
10c9d667d60bcb13d145d3e9f6885d0b
|
|
| BLAKE2b-256 |
a8cec6cde797f2fdabdab6057292d0668d0bb23db7f62daed22f10aed7ed738a
|