Metadata-Version: 2.5
Name: corent
Version: 0.7.0
Summary: Official Python SDK for Corent: one API for AI image, video, voice, and text generation with built-in routing, failover, and exact receipts.
Project-URL: Homepage, https://corent.tech
Project-URL: Documentation, https://corent.tech/docs
Author-email: Corent <krrocicrypto@gmail.com>
License-Expression: MIT
Keywords: ai,api,corent,image-generation,llm,text-to-speech,video-generation
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Requires-Dist: httpx>=0.24
Description-Content-Type: text/markdown

# Corent Python SDK

One API for AI **image, video, voice, and text** generation. You describe what you want; Corent picks the model that does the job well for the least money, reroutes failures, checks the output, and returns the exact charge on every response.

```bash
pip install corent
```

```python
from corent import Corent

client = Corent("co_live_...")  # get a key at https://corent.tech

image = client.images.generate("a lighthouse at dusk", aspect_ratio="9:16")
print(image.url, image.width, image.height, image.cost_cents)
print(image.hint)  # Made with Corent's default. Add "quality": "best" for top quality.

video = client.videos.generate("a paper boat drifting across a puddle",
                               quality="best", duration_s=5, resolution="1080p")
print(video.url, video.resolution, video.cost_cents)

speech = client.speech.generate("Welcome to Corent.")
print(speech.url, speech.cost_cents)

answer = client.text.generate("Name three uses for a paperclip.")
print(answer.text, answer.cost_cents)
```

## Quality and price: one switch, one limit

Every generate call takes two optional settings:

- **`quality="best"`** uses the top model for the job. It costs more. Leave it
  out and Corent picks the model that does the job well for the least money.
- **`max_cost_cents`** is the most one request may cost. If no model can do
  it for that, the call is refused before anything is spent
  (`InvalidRequestError`), and the charge never goes above it.

```python
image = client.images.generate("a product shot of a watch", quality="best", max_cost_cents=20)
print(image.quality, image.quality_source, image.hint)  # "best", "request", "Made with best quality."
```

Name a model in an image, video or speech prompt and Corent uses it when it
can do the job, taking those words out before it renders:

```python
clip = client.videos.generate("use seedance 2.5: a fox running in snow")
print(clip.model_from_prompt)  # ModelFromPrompt(name='Seedance 2.5', used=True, reason=None)
```

If it can't (the model can't do this job, costs more than your limit, or is
down), Corent picks another and `model_from_prompt.reason` says why. Text chat
never reads model names out of a prompt; pass `model=` there.

Every result carries `quality` (`"default"` or `"best"`), `quality_source`
(`"request"`, `"key"`, `"prompt"`, `"legacy"` or `"default"`) and a one-line
`hint`.

**Upgrading from 0.6:** kit 0.6.0 could not send `quality` or
`max_cost_cents` at all (the two arrived with the API's quality release), so
code on 0.6.0 that passes them has them silently dropped: upgrade first.
`tier=` still works and still goes out, with a `DeprecationWarning`. Swap it
for `quality="best"` where you wanted the top models, and drop it everywhere
else. The full list of changes is in [CHANGELOG.md](CHANGELOG.md).

## Make a film from one sentence

Describe the film and Corent writes the script, creates the characters once,
shoots every scene with them speaking their lines, checks quality, reshoots a
bad scene once, and joins it all into one video with sound. The price comes
first and costs nothing; the charge never goes above `max_cost_cents`.

```python
plan = client.films.plan(
    "a 50 second parody where 5 founders start a startup, know nothing, "
    "and the AI part is easy because they used Corent",
    duration_s=50,
)
print(plan.title, f"${plan.estimated_cost_cents / 100:.2f}")
for scene in plan.scenes:
    print(scene.index, scene.action, scene.dialogue)

film = client.films.create(plan.plan_id, max_cost_cents=int(plan.estimated_cost_cents * 1.1) + 1)
film = client.films.wait(film.film_id)   # checks every 60 seconds; about 15 to 30 minutes
print(film.video_url, film.cost_cents)
```

A 50 second 720p film with five characters costs roughly $10 to $20.
`wait()` raises `GenerationFailedError` if the film failed and
`FilmCancelledError` if it was cancelled. `client.films.get(id)` shows the
stage (`writing`, `casting`, `shooting`, `checking`, `editing`),
`client.films.cancel(id)` stops one, and `client.films.list(customer_id=...)`
lists them.

## What the SDK handles for you

- **Timeout-safe renders:** images submit as background jobs and are polled; a network hiccup can never lose a finished (and billed) result.
- **Safe retries:** every paid call (images, video, speech, text, batches, films) carries an auto idempotency key; retries on the media, batch and film calls can never double-charge.
- **Backoff:** 429/5xx are retried with `Retry-After` respected.
- **Honest receipts:** `width`/`height` are the *measured* pixels of the delivered file, and `cost_cents` is the exact charge.

## Pick a model yourself (direct access)

Pass `model` to pin an exact model. It is never substituted: if that model
can't deliver, the call fails. `max_cost_cents` still applies; `quality` does
not.

```python
image = client.images.generate("a lighthouse at dusk", model="corent-flux-schnell")
print(image.model)  # "corent-flux-schnell", the name you asked for

client.models()  # the menu: every model with its kind, quality and live status
```

Every name Corent publishes is spelled `corent-…`, that is what `client.models()`
lists, what comes back on `.model`, and what your receipt shows. Older spellings
you may already have hard-coded (`flux-schnell`, `seedream-5.0-direct`) still
resolve, and the SDK sends whatever string you pass straight through, untouched.

Each model has one name. When we can reach it by more than one route, Corent
serves whichever is cheapest at that moment and charges you that price, you
never have to shop between near-identical entries.

## Text (language models)

Every frontier lab on one key and one bill, priced per token.

```python
answer = client.text.generate("Explain reserve-then-settle billing.",
                              system="Answer in two sentences.", quality="best")
print(answer.text, answer.prompt_tokens, answer.completion_tokens, answer.cost_cents)

# a real conversation, tool calls included
reply = client.text.chat(
    [{"role": "system", "content": "Be terse."},
     {"role": "user", "content": "What's the weather?"}],
    model="corent-claude-opus-5",
)
```

Streaming isn't wrapped here: point any OpenAI-compatible client at
`https://api.corent.tech/v1` with your Corent key and it works as-is.

## Keep a character or product consistent

Pass 1 to 4 reference images and the prompt is applied as an *edit* of them, so
the same face, character, or product carries into a new scene.

```python
shot = client.images.generate(
    "the same woman, now on a beach",
    reference_image_urls=["https://cdn.example/her.png"],
)
```

Corent sends it only to a model that can edit. `client.models()` reports
`supports_reference_images` per model.

## A consistent character across shots

Make the character once as an image, then hand that image to every clip as a
reference (`@Image 1` in the prompt) on `corent-seedance-2.5`. Ask for the last
frame back and start the next clip from it, so one shot flows into the next
without a cut. Photos of real people are rejected as references; characters
generated inside Corent are accepted.

```python
hero = client.images.generate("a young sailor in a yellow raincoat, portrait, plain background", quality="best")

shot1 = client.videos.generate(
    "@Image 1 pushes a rowing boat off a misty shore",
    model="corent-seedance-2.5",
    reference_image_urls=[hero.url],
    aspect_ratio="16:9", duration_s=8,
    return_last_frame=True,
)
shot2 = client.videos.generate(
    "the boat drifts out and she looks back at the shore",
    image_url=shot1.last_frame_url,   # the exact frame shot 1 ended on
    return_last_frame=True,
)
print(shot1.url, shot2.url, shot2.duration_s)
```

`reference_video_urls` (up to 10, `@Video 1`) and `reference_audio_urls`
(up to 10, `@Audio 1`) work the same way, and `task` says what to do with
them: `auto`, `reference`, `edit`, `extend`. Two helpers cover the last two:

```python
fixed = client.videos.edit("Replace the red car in @Video 1 with a blue bicycle", shot1.url)
longer = client.videos.extend("she reaches the far bank and climbs out", shot2.url, duration_s=5)
```

An edit keeps the source clip's length and shape, an extension keeps its
shape, and first/last frame (`image_url` / `end_image_url`) cannot be mixed
with references; the SDK raises `InvalidRequestError` before anything is
sent. `client.models()` reports `supports_reference_images`,
`supports_video_edit`, `supports_video_extend` and `supports_last_frame` per
video model.

## Upload your own files

Hand Corent a file from disk (or raw bytes) and get back a URL every other
call accepts: `image_url`, `mask_url`, `source_image_url`, `reference_image_urls`.
Up to 25 MB. The type is read from the filename's extension unless you name one.

```python
up = client.upload("product.png")            # or bytes: client.upload(data, filename="product.png")
print(up.url, up.content_type, up.bytes)

client.images.generate("the same bottle, on a beach", reference_image_urls=[up.url])
client.videos.generate("the bottle slowly rotates", image_url=up.url)
```

## Image tools: upscale, cut out, edit

Synchronous. Upscaling is flat-priced; the cut-out and the masked edit are
priced on whichever supplier serves the call, so `cost_cents` on the result is
the price (there is no quote to read beforehand). The key's
`default_max_cost_cents` still applies: the price never goes above it. The
edit keeps every pixel outside the mask exactly as you sent it, which is what
an ad needs when the product carries someone else's trademark.

```python
bigger = client.images.upscale(up.url, prompt="keep the film grain")   # prompt optional
cutout = client.images.remove_background(up.url)                        # PNG, real transparency
mask = client.upload("mask.png")                                        # white = regenerate, black = keep
edited = client.images.edit(up.url, mask.url, "a red can instead of the blue one")
print(edited.url, edited.cost_cents)
```

## Tag spend by your own customer

Pass `customer_id` (1 to 128 characters, your own id) on any generating call
and `client.usage(customer_id=...)` reports that customer's spend alone.
Batches take a default plus a per-item override.

```python
client.images.generate("a fox", customer_id="acct_8812")
client.text.generate("Summarise this.", customer_id="acct_8812")
client.batches.images(
    [ImageBatchItem(prompt="a"), ImageBatchItem(prompt="b", customer_id="acct_other")],
    customer_id="acct_8812",  # default for every item without its own
)
client.usage(customer_id="acct_8812")
```

## Batches and webhooks

```python
from corent import ImageBatchItem, VideoBatchItem

# Up to 50 renders in one call. Each item bills at the normal rate.
batch = client.batches.images(
    [ImageBatchItem(prompt="a fox", max_cost_cents=5), ImageBatchItem(prompt="a heron", quality="best")],
    idempotency_key="campaign-9",  # a retry replays instead of re-billing
)
progress = client.batches.progress(batch.batch_id)

# Video items take everything a single clip takes: their own quality and
# max_cost_cents, an exact model, sound (audio) and a seed, per item.
clips = client.batches.videos([
    VideoBatchItem(prompt="a fox in snow", model="corent-seedance-2.5", audio=True, seed=7, duration_s=5),
    VideoBatchItem(prompt="a heron at dawn", quality="best", max_cost_cents=120),
])

# Or have the server deliver each result and skip polling entirely.
client.videos.generate(
    "a drone shot",
    max_cost_cents=100,
    webhook_url="https://your-server.com/webhooks/corent",
    webhook_secret="your_shared_secret",  # signs every delivery
)
```

## What else you can ask for

```python
# Repeat an image exactly, then change one thing.
first = client.images.generate("a fox in a library", model="corent-flux-schnell")
again = client.images.generate(
    "a fox in a library, wearing glasses",
    model="corent-flux-schnell", seed=12345, negative_prompt="text, watermark",
)

# Four versions of one prompt in one call (four real renders, four charges).
images = client.images.generate_many("a fox", 4)

# A transparent logo, at a size you choose.
client.images.generate("a minimal fox mark", transparent=True, width=1024, height=1024)

# Video with sound, going from one picture to another.
client.videos.generate(
    "the camera pulls back",
    audio=True,
    image_url="https://.../start.png", end_image_url="https://.../end.png",
    camera="zoom_out",
)

# Pick a voice, and shape how it reads.
voices = client.voices()
client.speech.generate("Welcome aboard.", voice_id=voices[0]["voice_id"], stability=0.3, speed=1.1)

# Started something by mistake? Stop it.
client.jobs.cancel(job.id)
```

`audio=True` routes only to models that actually render sound, so a silent
model can never quietly serve the request. Not every model takes every
setting: anything the chosen one could not honor comes back in `meta`
rather than being silently ignored.

## Fine-grained control

```python
job = client.images.generate("...", quality="best", wait=False)  # returns immediately
job = client.jobs.wait(job.id)                                    # resume any time

client.status()    # the four lanes: image, video, text, voice
client.models()    # direct-access menu (names, quality, status)
client.balance()   # Balance(balance_cents, held_cents, available_cents)
client.usage()     # what this account has spent; customer_id= narrows it
```

Every paid call sends an `Idempotency-Key`, so the SDK's own retries can
never double-charge on images, video, speech, batches and films (the API
replays the first result for a repeated key). Text calls carry one too; the
API is adding the replay there, so until then the kit does not retry a text
call after a network error. Pass your own `idempotency_key` to make that
survive a process restart too.

## Set it once per key

A key can carry its own defaults, so every request on it gets them without
code changes: `default_quality="best"` and `default_max_cost_cents`. A
request's own `quality` still wins. The key's `default_max_cost_cents` is a
ceiling: a request's `max_cost_cents` can ask for less, never more. Either
limit covers the whole request: a call for four images stays under it all
together.

The same two settings are on each key in the console: open **API keys**, then
the key ("Always best quality" and "Price limit (per request)"). To change them from
code you need a management key, not an ordinary API key: in the console, open
**Management keys** (in the sidebar under Account, or
https://corent.tech/dashboard/management-keys) and choose **New management
key**. A management key manages keys and reads usage; it cannot generate
anything.

```python
admin = Corent("co_live_management_key")

key = admin.create_api_key("production")       # can do everything, no limits set
print(key.api_key)                             # shown once: store it now

admin.update_api_key(key.id, default_quality="best", default_max_cost_cents=50)
admin.update_api_key(key.id, default_quality=None)   # None clears a setting
admin.list_api_keys()
admin.revoke_api_key(key.id)
```

A change reaches requests within a minute.

## Keep data in the US and Europe

Each key also has a switch, "Keep my data in the US and Europe", off by
default. With it on, requests on that key only use suppliers whose company and
machines are in the US, the UK, the EU or EEA, or Switzerland. Few models
qualify today (text on the Claude models that can stay in the US); any other
request on that key is refused with a 400 before anything is charged, and is
never sent elsewhere. Turn it on in the console under **API keys**, or with a
management key:

```bash
curl -X PATCH https://api.corent.tech/v1/account/api-keys/KEY_ID \
  -H "Authorization: Bearer $CORENT_MANAGEMENT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"data_region": "us_europe"}'
```

`null` turns it off. `update_api_key` does not take this setting yet. Every
supplier, what it receives, where it is and what it keeps:
[corent.tech/security](https://corent.tech/security).

Pricing: [corent.tech/pricing](https://corent.tech/pricing). Models: [corent.tech/models](https://corent.tech/models). Docs: [corent.tech/docs](https://corent.tech/docs).
