reflex-sdk 0.1.0__py3-none-any.whl

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.
reflex/__init__.py ADDED
@@ -0,0 +1,63 @@
1
+ """Public Reflex SDK."""
2
+
3
+ from .actions import ActionStream, action, connect, infer_actions, observation
4
+ from .client import Client
5
+ from .datasets import (
6
+ complete_dataset,
7
+ create_dataset,
8
+ get_dataset,
9
+ list_datasets,
10
+ register_huggingface_dataset,
11
+ upload_dataset,
12
+ validate_dataset,
13
+ )
14
+ from .instances import instance_status, provision_instance, teardown_instance
15
+ from .training import (
16
+ AdamParams,
17
+ AdapterHandle,
18
+ Datum,
19
+ ForwardBackwardResult,
20
+ LoraTrainingClient,
21
+ OptimStepResult,
22
+ ServiceClient,
23
+ cancel_training_job,
24
+ create_training_job,
25
+ full_finetune,
26
+ full_train,
27
+ get_training_job,
28
+ list_training_jobs,
29
+ lora_finetune,
30
+ )
31
+
32
+ __all__ = [
33
+ "ActionStream",
34
+ "AdamParams",
35
+ "AdapterHandle",
36
+ "Client",
37
+ "Datum",
38
+ "ForwardBackwardResult",
39
+ "LoraTrainingClient",
40
+ "OptimStepResult",
41
+ "ServiceClient",
42
+ "action",
43
+ "cancel_training_job",
44
+ "complete_dataset",
45
+ "connect",
46
+ "create_dataset",
47
+ "create_training_job",
48
+ "full_finetune",
49
+ "full_train",
50
+ "get_dataset",
51
+ "get_training_job",
52
+ "infer_actions",
53
+ "instance_status",
54
+ "list_training_jobs",
55
+ "list_datasets",
56
+ "lora_finetune",
57
+ "observation",
58
+ "provision_instance",
59
+ "register_huggingface_dataset",
60
+ "teardown_instance",
61
+ "upload_dataset",
62
+ "validate_dataset",
63
+ ]
reflex/_convex.py ADDED
@@ -0,0 +1,140 @@
1
+ """Minimal Convex HTTP client for public API-key SDK calls."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import base64
6
+ import json
7
+ import math
8
+ import os
9
+ import struct
10
+ from pathlib import Path
11
+ from typing import Any
12
+ from urllib import error, parse, request
13
+
14
+ DEFAULT_CONVEX_URL = "https://kindly-bullfrog-494.convex.cloud"
15
+
16
+
17
+ def convex_url(value: str | None = None) -> str:
18
+ resolved = (
19
+ value
20
+ or os.environ.get("REFLEX_CONVEX_URL", "")
21
+ or os.environ.get("CONVEX_URL", "")
22
+ or os.environ.get("NEXT_PUBLIC_CONVEX_URL", "")
23
+ or DEFAULT_CONVEX_URL
24
+ ).strip().rstrip("/")
25
+ parsed = parse.urlparse(resolved)
26
+ if parsed.scheme not in {"http", "https"} or not parsed.hostname:
27
+ raise ValueError("Convex URL must start with http:// or https://.")
28
+ return resolved
29
+
30
+
31
+ def _load_local_env(path: str | Path = ".env") -> None:
32
+ env_path = Path(path)
33
+ if not env_path.exists():
34
+ return
35
+ for line in env_path.read_text().splitlines():
36
+ stripped = line.strip()
37
+ if not stripped or stripped.startswith("#") or "=" not in stripped:
38
+ continue
39
+ key, value = stripped.split("=", 1)
40
+ os.environ.setdefault(key.strip(), value.strip().strip("\"'"))
41
+
42
+
43
+ _load_local_env()
44
+
45
+
46
+ def _convex_to_json(value: Any) -> Any:
47
+ if value is None or isinstance(value, (bool, str)):
48
+ return value
49
+ if isinstance(value, int):
50
+ return value
51
+ if isinstance(value, float):
52
+ if math.isnan(value) or math.isinf(value) or value == 0.0 and math.copysign(1.0, value) < 0:
53
+ return {"$float": base64.b64encode(struct.pack("<d", value)).decode("ascii")}
54
+ return value
55
+ if isinstance(value, (bytes, bytearray)):
56
+ return {"$bytes": base64.b64encode(bytes(value)).decode("ascii")}
57
+ if isinstance(value, (list, tuple)):
58
+ return [_convex_to_json(item) for item in value]
59
+ if isinstance(value, dict):
60
+ return {
61
+ str(key): _convex_to_json(item)
62
+ for key, item in sorted(value.items(), key=lambda entry: str(entry[0]))
63
+ if item is not None
64
+ }
65
+ raise TypeError(f"{type(value).__name__} is not a supported Convex value")
66
+
67
+
68
+ def _json_to_convex(value: Any) -> Any:
69
+ if value is None or isinstance(value, (bool, int, float, str)):
70
+ return value
71
+ if isinstance(value, list):
72
+ return [_json_to_convex(item) for item in value]
73
+ if isinstance(value, dict):
74
+ if set(value) == {"$integer"}:
75
+ return int.from_bytes(base64.b64decode(str(value["$integer"])), "little", signed=True)
76
+ if set(value) == {"$float"}:
77
+ return struct.unpack("<d", base64.b64decode(str(value["$float"])))[0]
78
+ if set(value) == {"$bytes"}:
79
+ return base64.b64decode(str(value["$bytes"]))
80
+ return {str(key): _json_to_convex(item) for key, item in value.items()}
81
+ return value
82
+
83
+
84
+ def convex_call(
85
+ kind: str,
86
+ function_name: str,
87
+ args: dict[str, Any] | None = None,
88
+ *,
89
+ url: str | None = None,
90
+ timeout: float = 60.0,
91
+ ) -> Any:
92
+ if kind not in {"action", "mutation", "query"}:
93
+ raise ValueError("Convex function kind must be action, mutation, or query.")
94
+ body = json.dumps(
95
+ {
96
+ "path": function_name,
97
+ "format": "convex_encoded_json",
98
+ "args": [_convex_to_json(args or {})],
99
+ }
100
+ ).encode("utf-8")
101
+ req = request.Request(
102
+ f"{convex_url(url)}/api/{kind}",
103
+ data=body,
104
+ method="POST",
105
+ headers={
106
+ "Content-Type": "application/json",
107
+ "Convex-Client": "python-reflex-sdk-0.1.0",
108
+ },
109
+ )
110
+ try:
111
+ with request.urlopen(req, timeout=timeout) as response:
112
+ raw = response.read().decode("utf-8")
113
+ except error.HTTPError as exc:
114
+ raw = exc.read().decode("utf-8", errors="replace")
115
+ if exc.code != 560:
116
+ raise RuntimeError(f"Convex {kind} {function_name} failed: {exc.code} {raw}".strip()) from exc
117
+ except error.URLError as exc:
118
+ raise RuntimeError(f"Failed to reach Convex: {exc.reason}") from exc
119
+
120
+ parsed = json.loads(raw) if raw else {}
121
+ if not isinstance(parsed, dict):
122
+ raise RuntimeError(f"Unexpected Convex response: {parsed!r}")
123
+ status = parsed.get("status")
124
+ if status == "success":
125
+ return _json_to_convex(parsed.get("value"))
126
+ if status == "error":
127
+ raise RuntimeError(str(parsed.get("errorMessage") or f"Convex {function_name} failed"))
128
+ raise RuntimeError(f"Unexpected Convex response: {parsed!r}")
129
+
130
+
131
+ def convex_action(function_name: str, args: dict[str, Any] | None = None, **kwargs: Any) -> Any:
132
+ return convex_call("action", function_name, args, **kwargs)
133
+
134
+
135
+ def convex_mutation(function_name: str, args: dict[str, Any] | None = None, **kwargs: Any) -> Any:
136
+ return convex_call("mutation", function_name, args, **kwargs)
137
+
138
+
139
+ def convex_query(function_name: str, args: dict[str, Any] | None = None, **kwargs: Any) -> Any:
140
+ return convex_call("query", function_name, args, **kwargs)
reflex/_transport.py ADDED
@@ -0,0 +1,86 @@
1
+ """Shared configuration and HTTP transport helpers for the public SDK."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ from pathlib import Path
8
+ from typing import Any
9
+ from urllib import error, parse, request
10
+
11
+ DEFAULT_API_URL = "https://api.tryreflex.ai"
12
+
13
+
14
+ def load_local_env(path: str | Path = ".env") -> None:
15
+ env_path = Path(path)
16
+ if not env_path.exists():
17
+ return
18
+ for line in env_path.read_text().splitlines():
19
+ stripped = line.strip()
20
+ if not stripped or stripped.startswith("#") or "=" not in stripped:
21
+ continue
22
+ key, value = stripped.split("=", 1)
23
+ os.environ.setdefault(key.strip(), value.strip().strip("\"'"))
24
+
25
+
26
+ load_local_env()
27
+
28
+
29
+ def api_key(value: str | None) -> str:
30
+ resolved = (
31
+ value
32
+ or os.environ.get("REFLEX_API_KEY", "")
33
+ or os.environ.get("RFX_API_KEY", "")
34
+ ).strip()
35
+ if not resolved:
36
+ raise ValueError("API key is required. Set api_key or REFLEX_API_KEY.")
37
+ return resolved
38
+
39
+
40
+ def base_url(value: str | None) -> str:
41
+ resolved = (
42
+ value
43
+ or os.environ.get("REFLEX_API_URL", "")
44
+ or os.environ.get("REFLEX_PLATFORM_URL", "")
45
+ or os.environ.get("RFX_API_URL", "")
46
+ or DEFAULT_API_URL
47
+ ).strip().rstrip("/")
48
+ parsed = parse.urlparse(resolved)
49
+ if parsed.scheme not in {"http", "https"} or not parsed.hostname:
50
+ raise ValueError("API URL must start with http:// or https://.")
51
+ return resolved
52
+
53
+
54
+ def request_json(
55
+ *,
56
+ url: str | None,
57
+ api_key_value: str | None,
58
+ path: str,
59
+ payload: dict[str, Any] | None = None,
60
+ method: str = "POST",
61
+ timeout: float = 30.0,
62
+ user_agent: str = "reflex-sdk/0.1.0",
63
+ ) -> dict[str, Any]:
64
+ body = json.dumps(payload).encode("utf-8") if payload is not None else None
65
+ req = request.Request(
66
+ parse.urljoin(f"{base_url(url)}/", path.lstrip("/")),
67
+ data=body,
68
+ method=method,
69
+ headers={
70
+ "Authorization": f"Bearer {api_key(api_key_value)}",
71
+ "User-Agent": user_agent,
72
+ **({"Content-Type": "application/json"} if payload is not None else {}),
73
+ },
74
+ )
75
+ try:
76
+ with request.urlopen(req, timeout=timeout) as response:
77
+ raw = response.read().decode("utf-8")
78
+ except error.HTTPError as exc:
79
+ detail = exc.read().decode("utf-8", errors="replace")
80
+ raise RuntimeError(f"{exc.code} {detail}".strip()) from exc
81
+ except error.URLError as exc:
82
+ raise RuntimeError(f"Failed to reach Reflex API: {exc.reason}") from exc
83
+ parsed = json.loads(raw) if raw else {}
84
+ if not isinstance(parsed, dict):
85
+ raise RuntimeError(f"Unexpected Reflex API response: {parsed!r}")
86
+ return parsed