olive-compute 0.1.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,63 @@
1
+ # Environment
2
+ .env
3
+ .env.local
4
+
5
+ # Rust
6
+ target/
7
+ *.pdb
8
+
9
+ # Python
10
+ __pycache__/
11
+ *.py[cod]
12
+ .venv/
13
+ venv/
14
+ .pytest_cache/
15
+ .mypy_cache/
16
+ *.egg-info/
17
+
18
+ # Node
19
+ node_modules/
20
+ dist/
21
+ build/
22
+ .next/
23
+
24
+ # Tauri runtime assets are downloaded during app CI builds.
25
+ app/src-tauri/resources/runtime/*
26
+ !app/src-tauri/resources/runtime/README.md
27
+
28
+ # Catalog staged into job-api build context (source of truth is /config/models.yaml)
29
+ services/job-api/config/
30
+
31
+ # IDE
32
+ .vscode/
33
+ .idea/
34
+ *.swp
35
+ .DS_Store
36
+
37
+ # Local data volumes
38
+ postgres_data/
39
+ redis_data/
40
+ grafana_data/
41
+ rabbitmq_data/
42
+ minio_data/
43
+
44
+ # Generated certs and keys
45
+ *.pem
46
+ *.key
47
+ *.crt
48
+ certs/
49
+ !**/example*.pem
50
+
51
+ # CDK
52
+ cdk.out/
53
+ infrastructure/node_modules/
54
+
55
+ # Benchmark outputs
56
+ *.benchmark.json
57
+ reports/
58
+
59
+ # Internal task orchestrator local state
60
+ .task-orchestrator/
61
+
62
+ # Local Claude Code project state
63
+ .claude/
@@ -0,0 +1,126 @@
1
+ Metadata-Version: 2.4
2
+ Name: olive-compute
3
+ Version: 0.1.1
4
+ Summary: Python client for the Olive distributed AI compute platform
5
+ Project-URL: Homepage, https://olivecompute.com
6
+ Project-URL: Documentation, https://olivecompute.com/docs
7
+ Project-URL: Repository, https://github.com/yotammos/olive
8
+ License: MIT
9
+ Keywords: ai,compute,distributed,embeddings,inference
10
+ Requires-Python: >=3.9
11
+ Requires-Dist: httpx>=0.27
12
+ Description-Content-Type: text/markdown
13
+
14
+ # Olive Python SDK
15
+
16
+ Distributed AI compute — embeddings and inference — with one import.
17
+
18
+ ```bash
19
+ pip install olive-compute
20
+ ```
21
+
22
+ ## Quickstart
23
+
24
+ ```python
25
+ from olive import OliveClient
26
+
27
+ client = OliveClient(api_key="olv_...")
28
+
29
+ # Embed text — uses the default embeddings model
30
+ vectors = client.embeddings(["hello world", "olive compute"])
31
+ print(vectors[0][:4]) # [0.0521, -0.1234, ...]
32
+
33
+ # Run inference — uses the default chat model
34
+ reply = client.inference("What is a neural network?", max_tokens=128)
35
+ print(reply)
36
+ ```
37
+
38
+ ## Choosing a model
39
+
40
+ Olive supports a catalog of curated open-source models. Browse them at
41
+ [olivecompute.com/models](https://olivecompute.com/models) or programmatically:
42
+
43
+ ```python
44
+ # List all available chat models
45
+ for m in client.list_models(modality="chat"):
46
+ print(m["id"], "—", m["pricing"]["input_per_1m_tokens_usd"], "/1M tokens")
47
+
48
+ # Get one model's full record
49
+ m = client.get_model("meta/llama-3.1-8b-instruct")
50
+ print(m["description"])
51
+ ```
52
+
53
+ Pass `model=` to any inference call to pin a specific model:
54
+
55
+ ```python
56
+ reply = client.inference(
57
+ "Write a Python function to reverse a list.",
58
+ model="qwen/qwen-2.5-coder-7b",
59
+ )
60
+
61
+ vectors = client.embeddings(
62
+ ["semantic search query"],
63
+ model="baai/bge-large-en-v1.5",
64
+ )
65
+ ```
66
+
67
+ If `model=` is omitted, Olive picks the default (featured) model for the workload.
68
+
69
+ ## Authentication
70
+
71
+ Get an API key from [provider.olivecompute.com](https://provider.olivecompute.com) → Settings → API Keys.
72
+
73
+ ```python
74
+ # API key (recommended)
75
+ client = OliveClient(api_key="olv_...")
76
+
77
+ # Email + password (issues a short-lived token automatically)
78
+ client = OliveClient(email="you@example.com", password="...")
79
+ ```
80
+
81
+ ## Compute tiers
82
+
83
+ | Tier | CPU | RAM | Use case |
84
+ |------|-----|-----|----------|
85
+ | `"light"` | 1 core | 2 GB | Embeddings, small inputs |
86
+ | `"medium"` | 2 cores | 4 GB | Standard inference (default) |
87
+ | `"heavy"` | 4 cores | 8 GB | Long context, large batches |
88
+
89
+ ## Async jobs
90
+
91
+ For long-running workloads, submit and poll separately:
92
+
93
+ ```python
94
+ job = client.submit_job(
95
+ workload_type="inference",
96
+ input_data='{"prompt": "Write a haiku", "max_tokens": 64}',
97
+ model="meta/llama-3.1-8b-instruct", # optional — default chat model otherwise
98
+ compute="medium",
99
+ )
100
+ print(job.id) # e3b2a1c0-...
101
+ print(job.status) # "running"
102
+
103
+ result = job.wait(timeout=120)
104
+ print(result["output_data"])
105
+ ```
106
+
107
+ ## Error handling
108
+
109
+ ```python
110
+ from olive import OliveClient, AuthError, JobError
111
+
112
+ try:
113
+ client = OliveClient(api_key="bad_key")
114
+ vectors = client.embeddings(["test"])
115
+ except AuthError:
116
+ print("Check your API key")
117
+ except JobError as e:
118
+ print(f"Job failed: {e}")
119
+ ```
120
+
121
+ ## Context manager
122
+
123
+ ```python
124
+ with OliveClient(api_key="olv_...") as client:
125
+ vectors = client.embeddings(["hello"])
126
+ ```
@@ -0,0 +1,113 @@
1
+ # Olive Python SDK
2
+
3
+ Distributed AI compute — embeddings and inference — with one import.
4
+
5
+ ```bash
6
+ pip install olive-compute
7
+ ```
8
+
9
+ ## Quickstart
10
+
11
+ ```python
12
+ from olive import OliveClient
13
+
14
+ client = OliveClient(api_key="olv_...")
15
+
16
+ # Embed text — uses the default embeddings model
17
+ vectors = client.embeddings(["hello world", "olive compute"])
18
+ print(vectors[0][:4]) # [0.0521, -0.1234, ...]
19
+
20
+ # Run inference — uses the default chat model
21
+ reply = client.inference("What is a neural network?", max_tokens=128)
22
+ print(reply)
23
+ ```
24
+
25
+ ## Choosing a model
26
+
27
+ Olive supports a catalog of curated open-source models. Browse them at
28
+ [olivecompute.com/models](https://olivecompute.com/models) or programmatically:
29
+
30
+ ```python
31
+ # List all available chat models
32
+ for m in client.list_models(modality="chat"):
33
+ print(m["id"], "—", m["pricing"]["input_per_1m_tokens_usd"], "/1M tokens")
34
+
35
+ # Get one model's full record
36
+ m = client.get_model("meta/llama-3.1-8b-instruct")
37
+ print(m["description"])
38
+ ```
39
+
40
+ Pass `model=` to any inference call to pin a specific model:
41
+
42
+ ```python
43
+ reply = client.inference(
44
+ "Write a Python function to reverse a list.",
45
+ model="qwen/qwen-2.5-coder-7b",
46
+ )
47
+
48
+ vectors = client.embeddings(
49
+ ["semantic search query"],
50
+ model="baai/bge-large-en-v1.5",
51
+ )
52
+ ```
53
+
54
+ If `model=` is omitted, Olive picks the default (featured) model for the workload.
55
+
56
+ ## Authentication
57
+
58
+ Get an API key from [provider.olivecompute.com](https://provider.olivecompute.com) → Settings → API Keys.
59
+
60
+ ```python
61
+ # API key (recommended)
62
+ client = OliveClient(api_key="olv_...")
63
+
64
+ # Email + password (issues a short-lived token automatically)
65
+ client = OliveClient(email="you@example.com", password="...")
66
+ ```
67
+
68
+ ## Compute tiers
69
+
70
+ | Tier | CPU | RAM | Use case |
71
+ |------|-----|-----|----------|
72
+ | `"light"` | 1 core | 2 GB | Embeddings, small inputs |
73
+ | `"medium"` | 2 cores | 4 GB | Standard inference (default) |
74
+ | `"heavy"` | 4 cores | 8 GB | Long context, large batches |
75
+
76
+ ## Async jobs
77
+
78
+ For long-running workloads, submit and poll separately:
79
+
80
+ ```python
81
+ job = client.submit_job(
82
+ workload_type="inference",
83
+ input_data='{"prompt": "Write a haiku", "max_tokens": 64}',
84
+ model="meta/llama-3.1-8b-instruct", # optional — default chat model otherwise
85
+ compute="medium",
86
+ )
87
+ print(job.id) # e3b2a1c0-...
88
+ print(job.status) # "running"
89
+
90
+ result = job.wait(timeout=120)
91
+ print(result["output_data"])
92
+ ```
93
+
94
+ ## Error handling
95
+
96
+ ```python
97
+ from olive import OliveClient, AuthError, JobError
98
+
99
+ try:
100
+ client = OliveClient(api_key="bad_key")
101
+ vectors = client.embeddings(["test"])
102
+ except AuthError:
103
+ print("Check your API key")
104
+ except JobError as e:
105
+ print(f"Job failed: {e}")
106
+ ```
107
+
108
+ ## Context manager
109
+
110
+ ```python
111
+ with OliveClient(api_key="olv_...") as client:
112
+ vectors = client.embeddings(["hello"])
113
+ ```
@@ -0,0 +1,16 @@
1
+ from .client import (
2
+ OliveClient, AsyncOliveClient,
3
+ Job, AsyncJob,
4
+ OliveError, AuthError, JobError,
5
+ NotFoundError, RateLimitError, ServerError,
6
+ )
7
+ from .compute import App, Image, Function, ComputeError
8
+
9
+ __all__ = [
10
+ "OliveClient", "AsyncOliveClient",
11
+ "Job", "AsyncJob",
12
+ "OliveError", "AuthError", "JobError",
13
+ "NotFoundError", "RateLimitError", "ServerError",
14
+ "App", "Image", "Function", "ComputeError",
15
+ ]
16
+ __version__ = "0.1.1"
@@ -0,0 +1,163 @@
1
+ """olive CLI — `olive deploy script.py`, `olive call`, `olive list`, etc.
2
+
3
+ Wired up via the [project.scripts] entry point in pyproject.toml.
4
+
5
+ Reads ``OLIVE_API_KEY`` from the environment (or accepts ``--api-key``).
6
+ Reads ``OLIVE_API_URL`` if overriding the default backend.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import argparse
12
+ import importlib.util
13
+ import json
14
+ import os
15
+ import sys
16
+ from pathlib import Path
17
+ from typing import Any
18
+
19
+ from .client import OliveClient
20
+ from .compute import App
21
+
22
+
23
+ def _load_module(path: str):
24
+ """Import a Python file by path. Returns the module (with side effects
25
+ from any top-level code, including ``@app.function`` registrations)."""
26
+ p = Path(path).resolve()
27
+ if not p.exists():
28
+ sys.exit(f"error: file not found: {path}")
29
+ spec = importlib.util.spec_from_file_location(p.stem, p)
30
+ if spec is None or spec.loader is None:
31
+ sys.exit(f"error: failed to import {path}")
32
+ mod = importlib.util.module_from_spec(spec)
33
+ spec.loader.exec_module(mod)
34
+ return mod
35
+
36
+
37
+ def _find_apps(mod) -> list[App]:
38
+ """Find all module-level App instances."""
39
+ return [v for v in vars(mod).values() if isinstance(v, App)]
40
+
41
+
42
+ def _client_from_args(args) -> OliveClient:
43
+ api_key = args.api_key or os.environ.get("OLIVE_API_KEY")
44
+ if not api_key:
45
+ sys.exit(
46
+ "error: no API key. Set OLIVE_API_KEY or pass --api-key. "
47
+ "Get one at https://customer.olivecompute.com"
48
+ )
49
+ base = args.api_url or os.environ.get("OLIVE_API_URL") or "https://api.olivecompute.com"
50
+ return OliveClient(api_key=api_key, base_url=base)
51
+
52
+
53
+ # ── Commands ──────────────────────────────────────────────────────────────
54
+
55
+
56
+ def cmd_deploy(args):
57
+ mod = _load_module(args.file)
58
+ apps = _find_apps(mod)
59
+ if not apps:
60
+ sys.exit(
61
+ f"error: no App found in {args.file}. Define one with "
62
+ "`from olive import App; app = App('my-app')`."
63
+ )
64
+ client = _client_from_args(args)
65
+ for app in apps:
66
+ if not app.functions:
67
+ print(f"⚠ app {app.name!r}: no @app.function decorators found")
68
+ continue
69
+ records = app.deploy(client)
70
+ print(f"✓ app {app.name!r}: deployed {len(records)} function(s)")
71
+ for fn, r in zip(app.functions, records):
72
+ print(f" • {fn.remote_name} → {r.get('function_id')}")
73
+ print(f" image: {r.get('image_uri')}")
74
+ print(f" invoke: olive call {fn.remote_name} '<json input>'")
75
+
76
+
77
+ def cmd_list(args):
78
+ client = _client_from_args(args)
79
+ fns = client.list_functions()
80
+ if not fns:
81
+ print("no compute functions deployed")
82
+ return
83
+ print(f"{'NAME':<40} {'TIER':<8} {'TIMEOUT':<8} {'INVOCATIONS':<12} {'VERSION'}")
84
+ for f in fns:
85
+ print(
86
+ f"{f['name']:<40} {f['compute_tier']:<8} {str(f['timeout_seconds'])+'s':<8} "
87
+ f"{str(f['invocation_count']):<12} {f['version']}"
88
+ )
89
+
90
+
91
+ def cmd_call(args):
92
+ client = _client_from_args(args)
93
+ try:
94
+ input_value: Any = json.loads(args.input) if args.input else None
95
+ except json.JSONDecodeError as e:
96
+ sys.exit(f"error: input is not valid JSON: {e}")
97
+ result = client.compute_call(args.name, input_value)
98
+ if isinstance(result, (dict, list)):
99
+ print(json.dumps(result, indent=2))
100
+ else:
101
+ print(result)
102
+
103
+
104
+ def cmd_spawn(args):
105
+ client = _client_from_args(args)
106
+ try:
107
+ input_value: Any = json.loads(args.input) if args.input else None
108
+ except json.JSONDecodeError as e:
109
+ sys.exit(f"error: input is not valid JSON: {e}")
110
+ r = client.compute_spawn(args.name, input_value)
111
+ print(json.dumps(r, indent=2))
112
+
113
+
114
+ def cmd_delete(args):
115
+ client = _client_from_args(args)
116
+ ok = client.delete_function(args.name)
117
+ if ok:
118
+ print(f"✓ deleted: {args.name}")
119
+ else:
120
+ sys.exit(f"error: function not found: {args.name}")
121
+
122
+
123
+ # ── Top-level parser ──────────────────────────────────────────────────────
124
+
125
+
126
+ def build_parser() -> argparse.ArgumentParser:
127
+ p = argparse.ArgumentParser(prog="olive", description="Olive Compute CLI")
128
+ p.add_argument("--api-key", help="Olive API key (default: $OLIVE_API_KEY)")
129
+ p.add_argument("--api-url", help="Olive API base URL (default: $OLIVE_API_URL or production)")
130
+
131
+ sub = p.add_subparsers(dest="cmd", required=True)
132
+
133
+ d = sub.add_parser("deploy", help="Deploy compute functions from a Python file")
134
+ d.add_argument("file", help="Path to script.py containing an App + @app.function decorators")
135
+ d.set_defaults(func=cmd_deploy)
136
+
137
+ l = sub.add_parser("list", help="List your deployed compute functions")
138
+ l.set_defaults(func=cmd_list)
139
+
140
+ c = sub.add_parser("call", help="Synchronously invoke a function")
141
+ c.add_argument("name", help="Function name (or function_id)")
142
+ c.add_argument("input", nargs="?", default="", help="JSON input value (optional)")
143
+ c.set_defaults(func=cmd_call)
144
+
145
+ s = sub.add_parser("spawn", help="Async invoke — returns job id immediately")
146
+ s.add_argument("name", help="Function name (or function_id)")
147
+ s.add_argument("input", nargs="?", default="", help="JSON input value (optional)")
148
+ s.set_defaults(func=cmd_spawn)
149
+
150
+ rm = sub.add_parser("delete", help="Delete a compute function")
151
+ rm.add_argument("name", help="Function name (or function_id)")
152
+ rm.set_defaults(func=cmd_delete)
153
+
154
+ return p
155
+
156
+
157
+ def main(argv: list[str] | None = None) -> None:
158
+ args = build_parser().parse_args(argv)
159
+ args.func(args)
160
+
161
+
162
+ if __name__ == "__main__":
163
+ main()