Metadata-Version: 2.4
Name: jijzeptai
Version: 0.3.0
Summary: Python client SDK for JijZept AI.
Keywords: jijzeptai,jijmodeling,ommx,optimization,solver,jijzeptsolver
Author: JIJ Inc.
Author-email: JIJ Inc. <info@j-ij.com>
License-Expression: Apache-2.0
License-File: LICENSE
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Typing :: Typed
Classifier: Topic :: Scientific/Engineering :: Mathematics
Requires-Dist: google-crc32c>=1.7,<2
Requires-Dist: jijmodeling>=2.5,<3
Requires-Dist: ommx>=2.6.1,<3
Requires-Dist: requests>=2.32,<3
Requires-Python: >=3.11, <3.15
Description-Content-Type: text/markdown

# jijzeptai

`jijzeptai` is the Python SDK for submitting optimization jobs and working with
Project files in JijZept AI. It supports CPython 3.11, 3.12, 3.13, and 3.14.

## Install

With uv:

```bash
uv add jijzeptai
```

With pip:

```bash
python -m pip install jijzeptai
```

JijModeling and OMMX are included as required dependencies.

## Create and configure an API token

In JijZept AI, open the account menu and choose API Tokens. Create a token for
the current Organization. The value appears only once, so copy it into your
environment with the production JijZept AI URL and do not put it in source
code:

```bash
export JIJZEPTAI_BASE_URL="https://ai.jijzept.com"
export JIJZEPTAI_API_TOKEN="jza_..."
```

`DeveloperClient` reads these variables by default. You can also pass
`base_url="https://ai.jijzept.com"` and `api_token` as constructor arguments
when that fits your program better.

Tokens created before this release used the `jzsa_` prefix. They are no longer
accepted. Revoke the legacy token, create a replacement in API Tokens, and
update `JIJZEPTAI_API_TOKEN` with the new `jza_` value.

## Project files

The client exposes Project listing, single-file transfers, selected-file
transfers, and directory-tree transfers. The Organization always comes from
the verified API token; these methods do not accept an Organization ID.

```python
import os

from jijzeptai import DeveloperClient

client = DeveloperClient()

projects = client.list_projects()
project_id = projects[0].id

page = client.list_project_files(project_id, path="/data", limit=100)
while page.has_more:
    page = client.list_project_files(
        project_id,
        path="/data",
        cursor=page.next_cursor,
        limit=100,
    )

client.create_directory(project_id, "/data", exist_ok=True)
uploaded = client.upload_file(
    project_id,
    local_path="input.csv",
    remote_path="/data/input.csv",
    overwrite=True,
)
saved_path = client.download_file(
    project_id,
    remote_path=uploaded.path,
    local_path="input-copy.csv",
    overwrite=True,
)
client.create_directory(project_id, "/workspace/src", parents=True, exist_ok=True)
selected = client.upload_files(
    project_id,
    ["pyproject.toml", "src/main.py"],
    remote_directory="/workspace",
)
tree = client.upload_directory(
    project_id,
    ".",
    "/workspace-tree",
    ignore_patterns=[".venv/", "dist/"],
)
os.makedirs("downloads", exist_ok=True)
downloaded = client.download_files(
    project_id,
    ["pyproject.toml", "src/main.py"],
    remote_directory="/workspace",
    local_directory="downloads",
)
downloaded_tree = client.download_directory(
    project_id,
    "/workspace-tree",
    "downloads/workspace-tree",
)
```

`list_project_files` accepts an absolute POSIX-style directory path. Its
`cursor` is opaque and belongs to the same path and pagination flow. Single-file
and multi-file methods use the same collection-shaped Developer APIs. One
upload call publishes one atomic FileTree commit within the documented
500-entry limit; an oversized selection fails before byte transfer and is not
split into partial commits. Upload refuses an existing remote path unless
`overwrite=True`. Download refuses an existing local path unless
`overwrite=True` and publishes the completed download atomically from a
temporary file. Directory methods preserve relative paths and explicit empty
directories, always exclude `.git/`, and reject symlinks and special entries.

Uploads and downloads use short-lived signed object-storage URLs internally.
The SDK never sends the JijZept AI `Authorization` header to those URLs.
Uploads bind a CRC32C checksum to the signed PUT, and downloads verify the
provider-measured CRC32C response header before atomically publishing the local
file.
If every retry loses the mutation outcome or ends in a retryable server error,
`ProjectFileMutationIndeterminateError` exposes the mutation Idempotency-Key.
The FileTree change may have committed; this error must not be treated as a
rollback.

## Minimal model workflow

This flow deploys a reusable JijModeling model, submits input data, waits for
optimization, and downloads the solution. Deploying a model requires an Admin
or Developer Organization Role. If your role is User, ask an Admin or Developer
to deploy the model and share its name and tag, then call
`submit_job_by_deployed_model` with those values (skip `deploy_model`).

```python
import jijmodeling as jm

from jijzeptai import DeveloperClient


# Define a reusable knapsack model.
@jm.Problem.define("knapsack", sense=jm.ProblemSense.MAXIMIZE)
def knapsack(problem: jm.DecoratedProblem):
    values = problem.Float(ndim=1)
    item_count = values.len_at(0)
    weights = problem.Float(shape=(item_count,))
    capacity = problem.Float()
    selected = problem.BinaryVar(
        "selected",
        shape=(item_count,),
        description="item selected",
    )
    problem += jm.sum(values * selected)
    problem += problem.Constraint(
        "capacity",
        jm.sum(weights * selected) <= capacity,
    )


# Create the SDK client for JijZept AI.
client = DeveloperClient()
# Publish the model so the Organization can run it with new input later.
model = client.deploy_model("knapsack", "demo", knapsack)
# Start an optimization job with concrete input values.
job = client.submit_job_by_deployed_model(
    model.name,
    model.tag,
    {
        "values": [10.0, 20.0, 15.0, 30.0],
        "weights": [2.0, 3.0, 5.0, 7.0],
        "capacity": 10.0,
    },
)
# Wait until the job succeeds, then download the solution.
finished = client.wait_for_success(job.job_id)
solution = client.download_solution(finished.job_id)
print(solution.extract_decision_variables("selected"))  # selected items
print(solution.objective)  # best objective value
print(solution.feasible)  # True when all constraints hold
```

## Organization roles

- Admin and Developer: deploy, update, archive, and unarchive models; submit a
  JijModeling problem or an OMMX problem file without deploying a model first;
  run deployed models.
- User: inspect and run deployed models.

Deployed models belong to the Organization. Jobs, results, and cancellation
stay private to the user who submitted the job.

## Errors

Catch `JijZeptAIError` for failures raised by this package, or
`JijZeptApiError` for an HTTP status, service error code, and response details.
Invalid arguments and local path or overwrite-policy violations raise
`TypeError` or `ValueError`. Authentication failures use HTTP 401, permission
failures use HTTP 403, stale FileTree writes use HTTP 409 or 412, and
unverifiable current identity or authorization state uses HTTP 503.

```python
from jijzeptai import (
    JijZeptAIError,
    JijZeptApiError,
    ProjectFileMutationIndeterminateError,
    TransportError,
)

try:
    uploaded = client.upload_file(
        project_id,
        local_path="input.csv",
        remote_path="/data/input.csv",
    )
except ProjectFileMutationIndeterminateError as error:
    print(f"Upload outcome unknown; idempotency key: {error.idempotency_key}")
except TransportError as error:
    print(f"Network request failed: {error}")
except JijZeptApiError as error:
    print(f"API request failed ({error.status_code}): {error.message}")
except JijZeptAIError as error:
    print(f"JijZept AI operation failed: {error}")
```

## License

- [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0)

Apache-2.0 covers the published `jijzeptai` package. It does not license the
JijZept AI hosted service, API tokens, user models or data, service terms, or
JIJ Inc. trademarks and the JijZept AI product name.
