lange-python 0.3.31__tar.gz → 0.5.0__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.
- lange_python-0.5.0/PKG-INFO +74 -0
- lange_python-0.5.0/README.md +53 -0
- lange_python-0.5.0/lange/__init__.py +4 -0
- {lange_python-0.3.31 → lange_python-0.5.0}/lange/cli/__init__.py +4 -0
- lange_python-0.5.0/lange/cli/_create.py +61 -0
- lange_python-0.5.0/lange/cli/_init.py +22 -0
- lange_python-0.5.0/lange/contracts/__init__.py +7 -0
- lange_python-0.5.0/lange/contracts/mesh/__init__.py +29 -0
- lange_python-0.5.0/lange/contracts/mesh/_message.py +32 -0
- lange_python-0.5.0/lange/contracts/mesh/ai/__init__.py +23 -0
- lange_python-0.5.0/lange/contracts/mesh/ai/ai_model.py +73 -0
- lange_python-0.5.0/lange/contracts/mesh/ai/chat.py +12 -0
- lange_python-0.5.0/lange/contracts/mesh/ai/embeddings.py +9 -0
- lange_python-0.5.0/lange/contracts/mesh/ai/worker_config.py +16 -0
- lange_python-0.5.0/lange/contracts/mesh/relay/__init__.py +9 -0
- lange_python-0.5.0/lange/contracts/mesh/relay/_rest.py +41 -0
- lange_python-0.5.0/lange/contracts/mesh/relay/_worker_config.py +31 -0
- {lange_python-0.3.31 → lange_python-0.5.0}/lange/distribution/__init__.py +0 -4
- lange_python-0.5.0/lange/mesh/__init__.py +5 -0
- lange_python-0.5.0/lange/mesh/relay/__init__.py +18 -0
- lange_python-0.5.0/lange/mesh/relay/client/__init__.py +5 -0
- lange_python-0.5.0/lange/mesh/relay/client/_client.py +593 -0
- {lange_python-0.3.31 → lange_python-0.5.0}/pyproject.toml +5 -3
- lange_python-0.3.31/PKG-INFO +0 -95
- lange_python-0.3.31/README.md +0 -76
- lange_python-0.3.31/lange/__init__.py +0 -6
- lange_python-0.3.31/lange/tunnel/__init__.py +0 -7
- lange_python-0.3.31/lange/tunnel/_client.py +0 -685
- lange_python-0.3.31/lange/tunnel/_util.py +0 -29
- {lange_python-0.3.31 → lange_python-0.5.0}/lange/__main__.py +0 -0
- {lange_python-0.3.31 → lange_python-0.5.0}/lange/_util/__init__.py +0 -0
- {lange_python-0.3.31 → lange_python-0.5.0}/lange/_util/_base_client.py +0 -0
- {lange_python-0.3.31 → lange_python-0.5.0}/lange/_util/_key_handling.py +0 -0
- {lange_python-0.3.31 → lange_python-0.5.0}/lange/cli/build/__init__.py +0 -0
- {lange_python-0.3.31 → lange_python-0.5.0}/lange/cli/build/_command.py +0 -0
- {lange_python-0.3.31 → lange_python-0.5.0}/lange/cli/build/_discovery.py +0 -0
- {lange_python-0.3.31 → lange_python-0.5.0}/lange/cli/build/_docker.py +0 -0
- {lange_python-0.3.31 → lange_python-0.5.0}/lange/cli/build/_poetry.py +0 -0
- {lange_python-0.3.31 → lange_python-0.5.0}/lange/cli/build/_types.py +0 -0
- {lange_python-0.3.31 → lange_python-0.5.0}/lange/cli/code/__init__.py +0 -0
- {lange_python-0.3.31 → lange_python-0.5.0}/lange/cli/code/_stats.py +0 -0
- {lange_python-0.3.31 → lange_python-0.5.0}/lange/cli/code/audit/__init__.py +0 -0
- {lange_python-0.3.31 → lange_python-0.5.0}/lange/cli/code/audit/_command.py +0 -0
- {lange_python-0.3.31 → lange_python-0.5.0}/lange/cli/code/audit/_discovery.py +0 -0
- {lange_python-0.3.31 → lange_python-0.5.0}/lange/cli/code/audit/_runner.py +0 -0
- {lange_python-0.3.31 → lange_python-0.5.0}/lange/cli/code/audit/_types.py +0 -0
- {lange_python-0.3.31 → lange_python-0.5.0}/lange/cli/distribution/__init__.py +0 -0
- {lange_python-0.3.31 → lange_python-0.5.0}/lange/cli/distribution/_command.py +0 -0
- {lange_python-0.3.31 → lange_python-0.5.0}/lange/distribution/_client.py +0 -0
- {lange_python-0.3.31 → lange_python-0.5.0}/lange/distribution/_update_macos.py +0 -0
- {lange_python-0.3.31 → lange_python-0.5.0}/lange/distribution/_util.py +0 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lange-python
|
|
3
|
+
Version: 0.5.0
|
|
4
|
+
Summary: A bundeld set of tools, clients for the lange-suite of tools and more.
|
|
5
|
+
Author: contact@robertlange.me
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
13
|
+
Requires-Dist: certifi (>=2026.0.0)
|
|
14
|
+
Requires-Dist: click (>=8.0.0,<9.0.0)
|
|
15
|
+
Requires-Dist: httpx (>=0.28.1,<0.29.0)
|
|
16
|
+
Requires-Dist: idna (>=3.15)
|
|
17
|
+
Requires-Dist: pydantic (>=2.0.0,<3.0.0)
|
|
18
|
+
Requires-Dist: websockets (>=12.0,<20.0)
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# lange-python
|
|
22
|
+
|
|
23
|
+
Python helpers for Lange services.
|
|
24
|
+
|
|
25
|
+
## Mesh Relay Worker
|
|
26
|
+
|
|
27
|
+
`MeshRelay` connects a local HTTP service to the Lange mesh relay and forwards
|
|
28
|
+
public relay requests to your local target.
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install lange-python
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
from lange.mesh.relay import MeshRelay
|
|
36
|
+
|
|
37
|
+
relay = MeshRelay(
|
|
38
|
+
host="wss://api.lange-labs.com",
|
|
39
|
+
key="default",
|
|
40
|
+
target="http://localhost:3000",
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
relay.start()
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
print(relay.status)
|
|
47
|
+
print(relay.remote_relay_address)
|
|
48
|
+
finally:
|
|
49
|
+
relay.stop()
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
By default, `MeshRelay` reads authentication from the `LANGE_LABS_API_KEY`
|
|
53
|
+
environment variable. Keep API keys in the environment or a local secret store
|
|
54
|
+
instead of hardcoding them in application code.
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
export LANGE_LABS_API_KEY="your-api-key"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The relay exposes lifecycle state for integrations:
|
|
61
|
+
|
|
62
|
+
- `status`: one of `unauthenticated`, `off`, `pending`, `connected`, or `failed`
|
|
63
|
+
- `connected`: whether the relay websocket is currently connected
|
|
64
|
+
- `remote_relay_address`: public REST relay address returned by the API
|
|
65
|
+
- `worker_config`: full worker configuration returned by the API
|
|
66
|
+
|
|
67
|
+
Reload authentication or reconnect the worker without rebuilding it:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
relay.reload()
|
|
71
|
+
relay.reload(api_key=None)
|
|
72
|
+
relay.reconnect()
|
|
73
|
+
```
|
|
74
|
+
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# lange-python
|
|
2
|
+
|
|
3
|
+
Python helpers for Lange services.
|
|
4
|
+
|
|
5
|
+
## Mesh Relay Worker
|
|
6
|
+
|
|
7
|
+
`MeshRelay` connects a local HTTP service to the Lange mesh relay and forwards
|
|
8
|
+
public relay requests to your local target.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pip install lange-python
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
from lange.mesh.relay import MeshRelay
|
|
16
|
+
|
|
17
|
+
relay = MeshRelay(
|
|
18
|
+
host="wss://api.lange-labs.com",
|
|
19
|
+
key="default",
|
|
20
|
+
target="http://localhost:3000",
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
relay.start()
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
print(relay.status)
|
|
27
|
+
print(relay.remote_relay_address)
|
|
28
|
+
finally:
|
|
29
|
+
relay.stop()
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
By default, `MeshRelay` reads authentication from the `LANGE_LABS_API_KEY`
|
|
33
|
+
environment variable. Keep API keys in the environment or a local secret store
|
|
34
|
+
instead of hardcoding them in application code.
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
export LANGE_LABS_API_KEY="your-api-key"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The relay exposes lifecycle state for integrations:
|
|
41
|
+
|
|
42
|
+
- `status`: one of `unauthenticated`, `off`, `pending`, `connected`, or `failed`
|
|
43
|
+
- `connected`: whether the relay websocket is currently connected
|
|
44
|
+
- `remote_relay_address`: public REST relay address returned by the API
|
|
45
|
+
- `worker_config`: full worker configuration returned by the API
|
|
46
|
+
|
|
47
|
+
Reload authentication or reconnect the worker without rebuilding it:
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
relay.reload()
|
|
51
|
+
relay.reload(api_key=None)
|
|
52
|
+
relay.reconnect()
|
|
53
|
+
```
|
|
@@ -6,7 +6,9 @@ import click
|
|
|
6
6
|
|
|
7
7
|
from .build import build_command
|
|
8
8
|
from .code import code_group
|
|
9
|
+
from ._create import create_command
|
|
9
10
|
from .distribution import distribution_group
|
|
11
|
+
from ._init import init_command
|
|
10
12
|
|
|
11
13
|
|
|
12
14
|
|
|
@@ -21,4 +23,6 @@ def cli() -> None:
|
|
|
21
23
|
|
|
22
24
|
cli.add_command(code_group, "code")
|
|
23
25
|
cli.add_command(build_command, "build")
|
|
26
|
+
cli.add_command(create_command, "create")
|
|
24
27
|
cli.add_command(distribution_group, "distribution")
|
|
28
|
+
cli.add_command(init_command, "init")
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Implementation for the ``lange create`` command."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
import click
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
SERVICES_FILE = Path("services.json")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _load_services(path: Path) -> list[dict[str, Any]]:
|
|
16
|
+
"""Load existing service definitions from disk.
|
|
17
|
+
|
|
18
|
+
:param path: JSON file containing service definitions.
|
|
19
|
+
:returns: Existing service list or an empty list when the file is absent.
|
|
20
|
+
"""
|
|
21
|
+
if not path.exists():
|
|
22
|
+
return []
|
|
23
|
+
|
|
24
|
+
raw_services = json.loads(path.read_text(encoding="utf-8"))
|
|
25
|
+
if isinstance(raw_services, list):
|
|
26
|
+
return [item for item in raw_services if isinstance(item, dict)]
|
|
27
|
+
if isinstance(raw_services, dict) and isinstance(raw_services.get("services"), list):
|
|
28
|
+
return [
|
|
29
|
+
item for item in raw_services["services"] if isinstance(item, dict)
|
|
30
|
+
]
|
|
31
|
+
raise click.ClickException("services.json must contain a list of services.")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _write_services(path: Path, services: list[dict[str, Any]]) -> None:
|
|
35
|
+
"""Persist service definitions with stable formatting.
|
|
36
|
+
|
|
37
|
+
:param path: JSON file to write.
|
|
38
|
+
:param services: Service definitions to persist.
|
|
39
|
+
"""
|
|
40
|
+
path.write_text(
|
|
41
|
+
json.dumps(services, indent=2, sort_keys=True) + "\n",
|
|
42
|
+
encoding="utf-8",
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@click.command("create")
|
|
47
|
+
def create_command() -> None:
|
|
48
|
+
"""Interactively create one service definition.
|
|
49
|
+
|
|
50
|
+
:returns: ``None``.
|
|
51
|
+
"""
|
|
52
|
+
service = {
|
|
53
|
+
"name": click.prompt("Service name", type=str),
|
|
54
|
+
"path": click.prompt("Service path", type=str),
|
|
55
|
+
"build_type": click.prompt("Build type", type=str),
|
|
56
|
+
"publish_path": click.prompt("Publish path", type=str),
|
|
57
|
+
}
|
|
58
|
+
services = _load_services(SERVICES_FILE)
|
|
59
|
+
services.append(service)
|
|
60
|
+
_write_services(SERVICES_FILE, services)
|
|
61
|
+
click.echo(f"Created service {service['name']}.")
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Implementation for the ``lange init`` command."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@click.command("init")
|
|
11
|
+
def init_command() -> None:
|
|
12
|
+
"""Create local ``.lange`` bootstrap files.
|
|
13
|
+
|
|
14
|
+
:returns: ``None``.
|
|
15
|
+
"""
|
|
16
|
+
lange_dir = Path(".lange")
|
|
17
|
+
lange_dir.mkdir(parents=True, exist_ok=True)
|
|
18
|
+
lange_dir.joinpath(".gitignore").write_text("*\n", encoding="utf-8")
|
|
19
|
+
secrets_file = lange_dir / "secrets.json"
|
|
20
|
+
if not secrets_file.exists():
|
|
21
|
+
secrets_file.write_text("{}\n", encoding="utf-8")
|
|
22
|
+
click.echo("Initialized .lange.")
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from ._message import MeshMessage
|
|
2
|
+
from .ai import (
|
|
3
|
+
AIModelSpecs,
|
|
4
|
+
AiModelConfig,
|
|
5
|
+
AiModelRegistration,
|
|
6
|
+
AiModelVirtualEnvironment,
|
|
7
|
+
ChatRequest,
|
|
8
|
+
ChatResponse,
|
|
9
|
+
EmbeddingRequest,
|
|
10
|
+
EmbeddingResponse,
|
|
11
|
+
MeshAiWorkerConfig,
|
|
12
|
+
)
|
|
13
|
+
from .relay import MeshRelayRequest, MeshRelayResponse, MeshRelayWorkerConfig
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"AIModelSpecs",
|
|
17
|
+
"AiModelConfig",
|
|
18
|
+
"AiModelRegistration",
|
|
19
|
+
"AiModelVirtualEnvironment",
|
|
20
|
+
"ChatRequest",
|
|
21
|
+
"ChatResponse",
|
|
22
|
+
"EmbeddingRequest",
|
|
23
|
+
"EmbeddingResponse",
|
|
24
|
+
"MeshAiWorkerConfig",
|
|
25
|
+
"MeshMessage",
|
|
26
|
+
"MeshRelayRequest",
|
|
27
|
+
"MeshRelayResponse",
|
|
28
|
+
"MeshRelayWorkerConfig",
|
|
29
|
+
]
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from typing import Literal
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
|
+
|
|
6
|
+
from .ai import EmbeddingResponse, EmbeddingRequest, ChatResponse, ChatRequest
|
|
7
|
+
from lange.contracts.mesh.relay import (
|
|
8
|
+
MeshRelayRequest,
|
|
9
|
+
MeshRelayResponse,
|
|
10
|
+
MeshRelayWorkerConfig,
|
|
11
|
+
MeshRelayWorkerRegistration,
|
|
12
|
+
)
|
|
13
|
+
from .ai import MeshAiWorkerConfig
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class MeshMessage(BaseModel):
|
|
17
|
+
id: uuid.UUID = Field(default_factory=uuid.uuid4)
|
|
18
|
+
status: Literal["hello", "ping", "ready", "request", "response"]
|
|
19
|
+
|
|
20
|
+
# data type
|
|
21
|
+
data: (
|
|
22
|
+
MeshAiWorkerConfig
|
|
23
|
+
| MeshRelayRequest
|
|
24
|
+
| MeshRelayResponse
|
|
25
|
+
| MeshRelayWorkerConfig
|
|
26
|
+
| MeshRelayWorkerRegistration
|
|
27
|
+
| EmbeddingRequest
|
|
28
|
+
| EmbeddingResponse
|
|
29
|
+
| ChatRequest
|
|
30
|
+
| ChatResponse
|
|
31
|
+
| None
|
|
32
|
+
)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from .embeddings import EmbeddingRequest, EmbeddingResponse
|
|
2
|
+
from .chat import ChatResponse, ChatRequest
|
|
3
|
+
from .ai_model import (
|
|
4
|
+
AiModelConfig,
|
|
5
|
+
AIModelSpecs,
|
|
6
|
+
AiModelRegistration,
|
|
7
|
+
AiModelVirtualEnvironment,
|
|
8
|
+
)
|
|
9
|
+
from .worker_config import MeshAiWorkerConfig
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"MeshAiWorkerConfig",
|
|
13
|
+
|
|
14
|
+
"EmbeddingRequest",
|
|
15
|
+
"EmbeddingResponse",
|
|
16
|
+
"AiModelConfig",
|
|
17
|
+
"AIModelSpecs",
|
|
18
|
+
"AiModelRegistration",
|
|
19
|
+
"AiModelVirtualEnvironment",
|
|
20
|
+
|
|
21
|
+
"ChatRequest",
|
|
22
|
+
"ChatResponse",
|
|
23
|
+
]
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class AIModelSpecs(BaseModel):
|
|
7
|
+
model_format: Literal["mlx", "vLLM"]
|
|
8
|
+
model_size_in_billions: int
|
|
9
|
+
quantization: str
|
|
10
|
+
model_id: str
|
|
11
|
+
model_hub: Literal["huggingface"]
|
|
12
|
+
model_revision: None
|
|
13
|
+
model_uri:str|None
|
|
14
|
+
activated_size_in_billions: int|None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class AiModelVirtualEnvironment(BaseModel):
|
|
18
|
+
packages: list[str]
|
|
19
|
+
inherit_pip_config: bool
|
|
20
|
+
index_url: str | None
|
|
21
|
+
extra_index_url: str | None
|
|
22
|
+
find_links: bool | None
|
|
23
|
+
trusted_host: None
|
|
24
|
+
no_build_isolation: None
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class AiModelRegistration(BaseModel):
|
|
28
|
+
version: int
|
|
29
|
+
context_length: int
|
|
30
|
+
model_name: str
|
|
31
|
+
model_lang: list[str]
|
|
32
|
+
model_ability: list[Literal["generate", "chat"]]
|
|
33
|
+
model_description: str
|
|
34
|
+
model_family: str
|
|
35
|
+
model_specs: list[AIModelSpecs]
|
|
36
|
+
chat_template: str|None
|
|
37
|
+
stop_token_ids: None
|
|
38
|
+
stop: None
|
|
39
|
+
cache_config: None
|
|
40
|
+
virtualenv: AiModelVirtualEnvironment
|
|
41
|
+
is_builtin: bool
|
|
42
|
+
|
|
43
|
+
reasoning_start_tag: str|None
|
|
44
|
+
reasoning_end_tag: str|None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class AiModelConfig(BaseModel):
|
|
48
|
+
# The name of the model to launch
|
|
49
|
+
model_name: str
|
|
50
|
+
|
|
51
|
+
# An easy alias for the model
|
|
52
|
+
model_alias: str
|
|
53
|
+
|
|
54
|
+
# The type of the model.
|
|
55
|
+
model_type: Literal["LLM", "embedding", "image", "audio", "video"]
|
|
56
|
+
|
|
57
|
+
# The size of the model in case multiple are available. e.G. gemma4 with 12b vs 31b
|
|
58
|
+
size: str | int | None = None
|
|
59
|
+
|
|
60
|
+
# The quantization of the model to use. Keep None for default.
|
|
61
|
+
quantization: str | None = None
|
|
62
|
+
|
|
63
|
+
# the context window of the model to use. if None it chooses default
|
|
64
|
+
context_window: int | None = None
|
|
65
|
+
|
|
66
|
+
# thinking
|
|
67
|
+
enable_thinking: bool | None = None
|
|
68
|
+
|
|
69
|
+
# engine configuration
|
|
70
|
+
model_engine: Literal["MLX", "vLLM"] | None = None
|
|
71
|
+
model_format: Literal["mlx", "vLLM"] | None = None
|
|
72
|
+
|
|
73
|
+
registration: AiModelRegistration | None
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from pydantic import BaseModel
|
|
2
|
+
from typing import Literal
|
|
3
|
+
|
|
4
|
+
class ChatMessage(BaseModel):
|
|
5
|
+
role: Literal["user", "assistant"]
|
|
6
|
+
content: str
|
|
7
|
+
|
|
8
|
+
class ChatRequest(BaseModel):
|
|
9
|
+
messages: list[ChatMessage]
|
|
10
|
+
|
|
11
|
+
class ChatResponse(BaseModel):
|
|
12
|
+
message: ChatMessage
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from pydantic import BaseModel
|
|
2
|
+
|
|
3
|
+
from .ai_model import AiModelConfig
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MeshAiWorkerConfig(BaseModel):
|
|
7
|
+
host: str = "0.0.0.0"
|
|
8
|
+
port: int = 8001
|
|
9
|
+
|
|
10
|
+
# inference config
|
|
11
|
+
chat_model: AiModelConfig | None = None
|
|
12
|
+
image_model: AiModelConfig | None = None
|
|
13
|
+
embedding_model: AiModelConfig | None = None
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MeshRelayRequest(BaseModel):
|
|
7
|
+
"""REST request payload sent to a mesh relay worker."""
|
|
8
|
+
|
|
9
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
10
|
+
|
|
11
|
+
method: str
|
|
12
|
+
path: str
|
|
13
|
+
headers: dict[str, str] = Field(default_factory=dict)
|
|
14
|
+
body: str | None = None
|
|
15
|
+
body_encoding: Literal["base64"] | None = Field(
|
|
16
|
+
default=None,
|
|
17
|
+
validation_alias="bodyEncoding",
|
|
18
|
+
serialization_alias="bodyEncoding",
|
|
19
|
+
)
|
|
20
|
+
query_params: dict[str, list[str]] = Field(
|
|
21
|
+
default_factory=dict,
|
|
22
|
+
validation_alias="queryParams",
|
|
23
|
+
serialization_alias="queryParams",
|
|
24
|
+
)
|
|
25
|
+
query_string: str | None = None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class MeshRelayResponse(BaseModel):
|
|
29
|
+
"""REST response payload returned by a mesh compute worker."""
|
|
30
|
+
|
|
31
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
32
|
+
|
|
33
|
+
status: int
|
|
34
|
+
headers: dict[str, str] = Field(default_factory=dict)
|
|
35
|
+
body: str | None = None
|
|
36
|
+
body_encoding: Literal["base64"] | None = Field(
|
|
37
|
+
default=None,
|
|
38
|
+
validation_alias="bodyEncoding",
|
|
39
|
+
serialization_alias="bodyEncoding",
|
|
40
|
+
)
|
|
41
|
+
error: str | None = None
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from typing import Literal
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MeshRelayWorkerRegistration(BaseModel):
|
|
7
|
+
"""Registration payload sent by a mesh relay worker during hello.
|
|
8
|
+
|
|
9
|
+
:param key: Public relay key owned by the worker.
|
|
10
|
+
:param request_timeout_seconds: Maximum seconds the relay should wait for
|
|
11
|
+
one worker-owned downstream request.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
model_config = ConfigDict(populate_by_name=True)
|
|
15
|
+
|
|
16
|
+
key: str
|
|
17
|
+
request_timeout_seconds: float = Field(
|
|
18
|
+
validation_alias="requestTimeoutSeconds",
|
|
19
|
+
serialization_alias="requestTimeoutSeconds",
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class MeshRelayWorkerConfig(BaseModel):
|
|
24
|
+
"""Runtime relay configuration returned by the API to a registered worker.
|
|
25
|
+
|
|
26
|
+
:param remote_relay_address: Public relay URL clients can call.
|
|
27
|
+
:param type: Relay protocol type.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
remote_relay_address: str
|
|
31
|
+
type: Literal["REST"]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Mesh relay client package."""
|
|
2
|
+
|
|
3
|
+
from .client import MeshRelay
|
|
4
|
+
|
|
5
|
+
from lange.contracts.mesh.relay import (
|
|
6
|
+
MeshRelayRequest,
|
|
7
|
+
MeshRelayResponse,
|
|
8
|
+
MeshRelayWorkerConfig,
|
|
9
|
+
MeshRelayWorkerRegistration,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"MeshRelay",
|
|
14
|
+
"MeshRelayRequest",
|
|
15
|
+
"MeshRelayResponse",
|
|
16
|
+
"MeshRelayWorkerConfig",
|
|
17
|
+
"MeshRelayWorkerRegistration",
|
|
18
|
+
]
|