lmnr 0.2.8__py3-none-any.whl → 0.2.10__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.
- lmnr/__init__.py +3 -7
- lmnr/cli/cli.py +42 -0
- lmnr/cli/parser/nodes/__init__.py +0 -5
- lmnr/cli/parser/nodes/code.py +10 -1
- lmnr/cli/parser/nodes/input.py +1 -2
- lmnr/cli/parser/nodes/json_extractor.py +29 -0
- lmnr/cli/parser/nodes/llm.py +7 -2
- lmnr/cli/parser/nodes/types.py +24 -6
- lmnr/cli/parser/parser.py +4 -0
- lmnr/cli/parser/utils.py +24 -0
- lmnr/cli/zip.py +12 -0
- lmnr/sdk/__init__.py +0 -0
- lmnr/sdk/registry.py +29 -0
- lmnr/sdk/remote_debugger.py +60 -64
- lmnr/types.py +10 -1
- {lmnr-0.2.8.dist-info → lmnr-0.2.10.dist-info}/METADATA +3 -1
- lmnr-0.2.10.dist-info/RECORD +28 -0
- lmnr-0.2.8.dist-info/RECORD +0 -24
- {lmnr-0.2.8.dist-info → lmnr-0.2.10.dist-info}/LICENSE +0 -0
- {lmnr-0.2.8.dist-info → lmnr-0.2.10.dist-info}/WHEEL +0 -0
- {lmnr-0.2.8.dist-info → lmnr-0.2.10.dist-info}/entry_points.txt +0 -0
lmnr/__init__.py
CHANGED
@@ -1,8 +1,4 @@
|
|
1
1
|
from .sdk.endpoint import Laminar
|
2
|
-
from .types import
|
3
|
-
|
4
|
-
|
5
|
-
EndpointRunResponse,
|
6
|
-
NodeInput
|
7
|
-
)
|
8
|
-
from .sdk.remote_debugger import RemoteDebugger as LaminarRemoteDebugger
|
2
|
+
from .types import ChatMessage, EndpointRunError, EndpointRunResponse, NodeInput
|
3
|
+
from .sdk.remote_debugger import RemoteDebugger as LaminarRemoteDebugger
|
4
|
+
from .sdk.registry import Registry as Pipeline
|
lmnr/cli/cli.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
from pathlib import Path
|
1
2
|
import requests
|
2
3
|
from dotenv import load_dotenv
|
3
4
|
import os
|
@@ -6,6 +7,8 @@ import logging
|
|
6
7
|
from cookiecutter.main import cookiecutter
|
7
8
|
from pydantic.alias_generators import to_pascal
|
8
9
|
|
10
|
+
from lmnr.cli.zip import zip_directory
|
11
|
+
|
9
12
|
from .parser.parser import runnable_graph_to_template_vars
|
10
13
|
|
11
14
|
logger = logging.getLogger(__name__)
|
@@ -92,3 +95,42 @@ def pull(pipeline_name, pipeline_version_name, project_api_key, loglevel):
|
|
92
95
|
no_input=True,
|
93
96
|
overwrite_if_exists=True,
|
94
97
|
)
|
98
|
+
|
99
|
+
|
100
|
+
@cli.command(name="deploy")
|
101
|
+
@click.argument("endpoint_id")
|
102
|
+
@click.option(
|
103
|
+
"-p",
|
104
|
+
"--project-api-key",
|
105
|
+
help="Project API key",
|
106
|
+
)
|
107
|
+
def deploy(endpoint_id, project_api_key):
|
108
|
+
project_api_key = project_api_key or os.environ.get("LMNR_PROJECT_API_KEY")
|
109
|
+
if not project_api_key:
|
110
|
+
load_dotenv()
|
111
|
+
project_api_key = os.environ.get("LMNR_PROJECT_API_KEY")
|
112
|
+
if not project_api_key:
|
113
|
+
raise ValueError("LMNR_PROJECT_API_KEY is not set")
|
114
|
+
|
115
|
+
current_directory = Path.cwd()
|
116
|
+
zip_file_path = current_directory / "archive.zip"
|
117
|
+
|
118
|
+
zip_directory(current_directory, zip_file_path)
|
119
|
+
|
120
|
+
try:
|
121
|
+
url = f"https://api.lmnr.ai/v2/endpoints/{endpoint_id}/deploy-code"
|
122
|
+
with open(zip_file_path, "rb") as f:
|
123
|
+
headers = {
|
124
|
+
"Authorization": f"Bearer {project_api_key}",
|
125
|
+
}
|
126
|
+
files = {"file": f}
|
127
|
+
response = requests.post(url, headers=headers, files=files)
|
128
|
+
|
129
|
+
if response.status_code != 200:
|
130
|
+
raise ValueError(
|
131
|
+
f"Error in deploying code: {response.status_code}\n{response.text}"
|
132
|
+
)
|
133
|
+
except Exception:
|
134
|
+
logging.exception("Error in deploying code")
|
135
|
+
finally:
|
136
|
+
Path.unlink(zip_file_path, missing_ok=True)
|
@@ -4,21 +4,16 @@ from typing import Optional
|
|
4
4
|
import uuid
|
5
5
|
|
6
6
|
|
7
|
-
HandleType = str # "String" | "ChatMessageList" | "Any"
|
8
|
-
|
9
|
-
|
10
7
|
@dataclass
|
11
8
|
class Handle:
|
12
9
|
id: uuid.UUID
|
13
10
|
name: Optional[str]
|
14
|
-
type: HandleType
|
15
11
|
|
16
12
|
@classmethod
|
17
13
|
def from_dict(cls, dict: dict) -> "Handle":
|
18
14
|
return cls(
|
19
15
|
id=uuid.UUID(dict["id"]),
|
20
16
|
name=(dict["name"] if "name" in dict else None),
|
21
|
-
type=dict["type"],
|
22
17
|
)
|
23
18
|
|
24
19
|
|
lmnr/cli/parser/nodes/code.py
CHANGED
@@ -12,6 +12,8 @@ class CodeNode(NodeFunctions):
|
|
12
12
|
inputs: list[Handle]
|
13
13
|
outputs: list[Handle]
|
14
14
|
inputs_mappings: dict[uuid.UUID, uuid.UUID]
|
15
|
+
code: str
|
16
|
+
fn_name: str
|
15
17
|
|
16
18
|
def handles_mapping(
|
17
19
|
self, output_handle_id_to_node_name: dict[str, str]
|
@@ -24,4 +26,11 @@ class CodeNode(NodeFunctions):
|
|
24
26
|
return "Code"
|
25
27
|
|
26
28
|
def config(self) -> dict:
|
27
|
-
return {
|
29
|
+
return {
|
30
|
+
"code": self.code,
|
31
|
+
"fn_name": self.fn_name,
|
32
|
+
"fn_inputs": ", ".join(
|
33
|
+
f"{handle.name}=input_to_code_node_arg({handle.name})"
|
34
|
+
for handle in self.inputs
|
35
|
+
),
|
36
|
+
}
|
lmnr/cli/parser/nodes/input.py
CHANGED
@@ -2,7 +2,7 @@ from dataclasses import dataclass
|
|
2
2
|
from typing import Optional
|
3
3
|
import uuid
|
4
4
|
|
5
|
-
from lmnr.cli.parser.nodes import Handle,
|
5
|
+
from lmnr.cli.parser.nodes import Handle, NodeFunctions
|
6
6
|
from lmnr.types import NodeInput
|
7
7
|
|
8
8
|
|
@@ -12,7 +12,6 @@ class InputNode(NodeFunctions):
|
|
12
12
|
name: str
|
13
13
|
outputs: list[Handle]
|
14
14
|
input: Optional[NodeInput]
|
15
|
-
input_type: HandleType
|
16
15
|
|
17
16
|
def handles_mapping(
|
18
17
|
self, output_handle_id_to_node_name: dict[str, str]
|
@@ -0,0 +1,29 @@
|
|
1
|
+
from dataclasses import dataclass
|
2
|
+
|
3
|
+
import uuid
|
4
|
+
|
5
|
+
from lmnr.cli.parser.nodes import Handle, NodeFunctions
|
6
|
+
from lmnr.cli.parser.utils import map_handles
|
7
|
+
|
8
|
+
|
9
|
+
@dataclass
|
10
|
+
class JsonExtractorNode(NodeFunctions):
|
11
|
+
id: uuid.UUID
|
12
|
+
name: str
|
13
|
+
inputs: list[Handle]
|
14
|
+
outputs: list[Handle]
|
15
|
+
inputs_mappings: dict[uuid.UUID, uuid.UUID]
|
16
|
+
template: str
|
17
|
+
|
18
|
+
def handles_mapping(
|
19
|
+
self, output_handle_id_to_node_name: dict[str, str]
|
20
|
+
) -> list[tuple[str, str]]:
|
21
|
+
return map_handles(
|
22
|
+
self.inputs, self.inputs_mappings, output_handle_id_to_node_name
|
23
|
+
)
|
24
|
+
|
25
|
+
def node_type(self) -> str:
|
26
|
+
return "JsonExtractor"
|
27
|
+
|
28
|
+
def config(self) -> dict:
|
29
|
+
return {"template": self.template}
|
lmnr/cli/parser/nodes/llm.py
CHANGED
@@ -44,8 +44,13 @@ class LLMNode(NodeFunctions):
|
|
44
44
|
"model": model,
|
45
45
|
"model_params": self.model_params,
|
46
46
|
"stream": self.stream,
|
47
|
-
"
|
47
|
+
"enable_structured_output": self.structured_output_enabled
|
48
|
+
and self.structured_output_schema is not None,
|
48
49
|
"structured_output_max_retries": self.structured_output_max_retries,
|
49
50
|
"structured_output_schema": self.structured_output_schema,
|
50
|
-
"
|
51
|
+
"structured_output_schema_target_str": (
|
52
|
+
"None"
|
53
|
+
if self.structured_output_schema_target is None
|
54
|
+
else f'"{self.structured_output_schema_target}"'
|
55
|
+
),
|
51
56
|
}
|
lmnr/cli/parser/nodes/types.py
CHANGED
@@ -5,6 +5,7 @@ from lmnr.cli.parser.nodes import Handle
|
|
5
5
|
from lmnr.cli.parser.nodes.code import CodeNode
|
6
6
|
from lmnr.cli.parser.nodes.condition import ConditionNode
|
7
7
|
from lmnr.cli.parser.nodes.input import InputNode
|
8
|
+
from lmnr.cli.parser.nodes.json_extractor import JsonExtractorNode
|
8
9
|
from lmnr.cli.parser.nodes.llm import LLMNode
|
9
10
|
from lmnr.cli.parser.nodes.output import OutputNode
|
10
11
|
from lmnr.cli.parser.nodes.router import Route, RouterNode
|
@@ -32,6 +33,7 @@ Node = Union[
|
|
32
33
|
RouterNode,
|
33
34
|
SemanticSearchNode,
|
34
35
|
CodeNode,
|
36
|
+
JsonExtractorNode,
|
35
37
|
]
|
36
38
|
|
37
39
|
|
@@ -42,7 +44,6 @@ def node_from_dict(node_dict: dict) -> Node:
|
|
42
44
|
name=node_dict["name"],
|
43
45
|
outputs=[Handle.from_dict(handle) for handle in node_dict["outputs"]],
|
44
46
|
input=node_input_from_json(node_dict["input"]),
|
45
|
-
input_type=node_dict["inputType"],
|
46
47
|
)
|
47
48
|
elif node_dict["type"] == "Output":
|
48
49
|
return OutputNode(
|
@@ -86,11 +87,14 @@ def node_from_dict(node_dict: dict) -> Node:
|
|
86
87
|
node_dict["modelParams"] if "modelParams" in node_dict else None
|
87
88
|
),
|
88
89
|
stream=False,
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
90
|
+
structured_output_enabled=node_dict.get("structuredOutputEnabled", False),
|
91
|
+
structured_output_max_retries=node_dict.get(
|
92
|
+
"structuredOutputMaxRetries", 0
|
93
|
+
),
|
94
|
+
structured_output_schema=node_dict.get("structuredOutputSchema", None),
|
95
|
+
structured_output_schema_target=node_dict.get(
|
96
|
+
"structuredOutputSchemaTarget", None
|
97
|
+
),
|
94
98
|
)
|
95
99
|
elif node_dict["type"] == "Router":
|
96
100
|
return RouterNode(
|
@@ -130,6 +134,20 @@ def node_from_dict(node_dict: dict) -> Node:
|
|
130
134
|
uuid.UUID(k): uuid.UUID(v)
|
131
135
|
for k, v in node_dict["inputsMappings"].items()
|
132
136
|
},
|
137
|
+
code=node_dict["code"],
|
138
|
+
fn_name=node_dict["fnName"],
|
139
|
+
)
|
140
|
+
elif node_dict["type"] == "JsonExtractor":
|
141
|
+
return JsonExtractorNode(
|
142
|
+
id=uuid.UUID(node_dict["id"]),
|
143
|
+
name=node_dict["name"],
|
144
|
+
inputs=[Handle.from_dict(handle) for handle in node_dict["inputs"]],
|
145
|
+
outputs=[Handle.from_dict(handle) for handle in node_dict["outputs"]],
|
146
|
+
inputs_mappings={
|
147
|
+
uuid.UUID(k): uuid.UUID(v)
|
148
|
+
for k, v in node_dict["inputsMappings"].items()
|
149
|
+
},
|
150
|
+
template=node_dict["template"],
|
133
151
|
)
|
134
152
|
else:
|
135
153
|
raise ValueError(f"Node type {node_dict['type']} not supported")
|
lmnr/cli/parser/parser.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
from lmnr.cli.parser.utils import replace_spaces_with_underscores
|
1
2
|
from .nodes.types import node_from_dict
|
2
3
|
|
3
4
|
|
@@ -8,6 +9,9 @@ def runnable_graph_to_template_vars(graph: dict) -> dict:
|
|
8
9
|
node_id_to_node_name = {}
|
9
10
|
output_handle_id_to_node_name: dict[str, str] = {}
|
10
11
|
for node in graph["nodes"].values():
|
12
|
+
# override node names in the graph itself to be safe
|
13
|
+
node["name"] = replace_spaces_with_underscores(node["name"])
|
14
|
+
|
11
15
|
node_id_to_node_name[node["id"]] = node["name"]
|
12
16
|
for handle in node["outputs"]:
|
13
17
|
output_handle_id_to_node_name[handle["id"]] = node["name"]
|
lmnr/cli/parser/utils.py
CHANGED
@@ -23,3 +23,27 @@ def map_handles(
|
|
23
23
|
(input_name, output_handle_id_to_node_name[str(output_id)])
|
24
24
|
for input_name, output_id in mapping
|
25
25
|
]
|
26
|
+
|
27
|
+
|
28
|
+
def replace_spaces_with_underscores(s: str):
|
29
|
+
spaces = [
|
30
|
+
"\u0020",
|
31
|
+
"\u00A0",
|
32
|
+
"\u1680",
|
33
|
+
"\u2000",
|
34
|
+
"\u2001",
|
35
|
+
"\u2002",
|
36
|
+
"\u2003",
|
37
|
+
"\u2004",
|
38
|
+
"\u2005",
|
39
|
+
"\u2006",
|
40
|
+
"\u2007",
|
41
|
+
"\u2008",
|
42
|
+
"\u2009",
|
43
|
+
"\u200A",
|
44
|
+
"\u200B",
|
45
|
+
"\u202F",
|
46
|
+
"\u205F",
|
47
|
+
"\u3000",
|
48
|
+
]
|
49
|
+
return s.translate({ord(space): "_" for space in spaces})
|
lmnr/cli/zip.py
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
import os
|
2
|
+
from pathlib import Path
|
3
|
+
import zipfile
|
4
|
+
|
5
|
+
|
6
|
+
def zip_directory(directory_path: Path, zip_file_path: Path):
|
7
|
+
with zipfile.ZipFile(zip_file_path, "w", zipfile.ZIP_DEFLATED) as zipf:
|
8
|
+
for root, _, files in os.walk(directory_path):
|
9
|
+
for file in files:
|
10
|
+
file_path = os.path.join(root, file)
|
11
|
+
arcname = os.path.relpath(file_path, directory_path)
|
12
|
+
zipf.write(file_path, arcname)
|
lmnr/sdk/__init__.py
ADDED
File without changes
|
lmnr/sdk/registry.py
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
from typing import Callable
|
2
|
+
|
3
|
+
from lmnr.types import NodeFunction, NodeInput
|
4
|
+
|
5
|
+
|
6
|
+
class Registry:
|
7
|
+
"""
|
8
|
+
Class to register and resolve node functions based on their node names.
|
9
|
+
|
10
|
+
Node names cannot have space in their name.
|
11
|
+
"""
|
12
|
+
|
13
|
+
functions: dict[str, NodeFunction]
|
14
|
+
|
15
|
+
def __init__(self):
|
16
|
+
self.functions = {}
|
17
|
+
|
18
|
+
def add(self, node_name: str, function: Callable[..., NodeInput]):
|
19
|
+
self.functions[node_name] = NodeFunction(node_name, function)
|
20
|
+
|
21
|
+
def func(self, node_name: str):
|
22
|
+
def decorator(f: Callable[..., NodeInput]):
|
23
|
+
self.add(node_name, f)
|
24
|
+
return f
|
25
|
+
|
26
|
+
return decorator
|
27
|
+
|
28
|
+
def get(self, node_name: str) -> Callable[..., NodeInput]:
|
29
|
+
return self.functions[node_name].function
|
lmnr/sdk/remote_debugger.py
CHANGED
@@ -1,34 +1,45 @@
|
|
1
|
-
from typing import Callable, Optional
|
1
|
+
from typing import Callable, Optional, Union
|
2
2
|
from websockets.sync.client import connect
|
3
3
|
import pydantic
|
4
4
|
import websockets
|
5
5
|
from lmnr.types import (
|
6
|
-
DeregisterDebuggerRequest,
|
7
|
-
|
6
|
+
DeregisterDebuggerRequest,
|
7
|
+
NodeFunction,
|
8
|
+
NodeInput,
|
9
|
+
RegisterDebuggerRequest,
|
10
|
+
SDKError,
|
11
|
+
ToolCallError,
|
12
|
+
ToolCallRequest,
|
13
|
+
ToolCallResponse,
|
8
14
|
)
|
9
15
|
import uuid
|
10
16
|
import json
|
11
17
|
from threading import Thread
|
12
18
|
|
19
|
+
|
13
20
|
class RemoteDebugger:
|
14
21
|
def __init__(
|
15
22
|
self,
|
16
23
|
project_api_key: str,
|
17
|
-
tools: list[Callable[..., NodeInput]] = []
|
24
|
+
tools: Union[dict[str, NodeFunction], list[Callable[..., NodeInput]]] = [],
|
18
25
|
):
|
26
|
+
# for simplicity and backwards compatibility, we allow the user to pass a list
|
27
|
+
if isinstance(tools, list):
|
28
|
+
tools = {f.__name__: NodeFunction(f.__name__, f) for f in tools}
|
29
|
+
|
19
30
|
self.project_api_key = project_api_key
|
20
|
-
self.url =
|
31
|
+
self.url = "wss://api.lmnr.ai/v2/endpoint/ws"
|
21
32
|
self.tools = tools
|
22
33
|
self.thread = Thread(target=self._run)
|
23
34
|
self.stop_flag = False
|
24
35
|
self.session = None
|
25
|
-
|
36
|
+
|
26
37
|
def start(self) -> Optional[str]:
|
27
38
|
self.stop_flag = False
|
28
39
|
self.session = self._generate_session_id()
|
29
40
|
self.thread.start()
|
30
41
|
return self.session
|
31
|
-
|
42
|
+
|
32
43
|
def stop(self):
|
33
44
|
self.stop_flag = True
|
34
45
|
self.thread.join()
|
@@ -37,17 +48,13 @@ class RemoteDebugger:
|
|
37
48
|
# a new thread
|
38
49
|
# in case the user wants to start the debugger again
|
39
50
|
self.thread = Thread(target=self._run)
|
40
|
-
|
41
|
-
def get_session_id(self) -> str:
|
42
|
-
return self.session
|
43
|
-
|
51
|
+
|
44
52
|
def _run(self):
|
53
|
+
assert self.session is not None, "Session ID not set"
|
45
54
|
request = RegisterDebuggerRequest(debuggerSessionId=self.session)
|
46
55
|
with connect(
|
47
56
|
self.url,
|
48
|
-
additional_headers={
|
49
|
-
'Authorization': f'Bearer {self.project_api_key}'
|
50
|
-
}
|
57
|
+
additional_headers={"Authorization": f"Bearer {self.project_api_key}"},
|
51
58
|
) as websocket:
|
52
59
|
websocket.send(request.model_dump_json())
|
53
60
|
print(self._format_session_id_and_registerd_functions())
|
@@ -55,7 +62,7 @@ class RemoteDebugger:
|
|
55
62
|
|
56
63
|
while not self.stop_flag:
|
57
64
|
try:
|
58
|
-
# blocks the thread until a message
|
65
|
+
# blocks the thread until a message
|
59
66
|
# is received or a timeout (3 seconds) occurs
|
60
67
|
message = websocket.recv(3)
|
61
68
|
except TimeoutError:
|
@@ -66,55 +73,48 @@ class RemoteDebugger:
|
|
66
73
|
try:
|
67
74
|
tool_call = ToolCallRequest.model_validate_json(message)
|
68
75
|
req_id = tool_call.reqId
|
69
|
-
except:
|
70
|
-
raise SDKError(f
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
'. Registered tools: ' +\
|
79
|
-
{", ".join([tool.__name__ for tool in self.tools])}
|
76
|
+
except Exception:
|
77
|
+
raise SDKError(f"Invalid message received:\n{message}")
|
78
|
+
matching_tool = self.tools.get(tool_call.toolCall.function.name)
|
79
|
+
if matching_tool is None:
|
80
|
+
error_message = (
|
81
|
+
f"Tool {tool_call.toolCall.function.name} not found"
|
82
|
+
+ ". Registered tools: "
|
83
|
+
+ ", ".join(self.tools.keys())
|
84
|
+
)
|
80
85
|
e = ToolCallError(error=error_message, reqId=req_id)
|
81
86
|
websocket.send(e.model_dump_json())
|
82
87
|
continue
|
83
|
-
tool =
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
response=response
|
105
|
-
)
|
106
|
-
except pydantic.ValidationError as e:
|
107
|
-
formatted_response = ToolCallResponse(
|
108
|
-
reqId=tool_call.reqId,
|
109
|
-
response=str(response)
|
110
|
-
)
|
111
|
-
websocket.send(
|
112
|
-
formatted_response.model_dump_json()
|
88
|
+
tool = matching_tool.function
|
89
|
+
|
90
|
+
# default the arguments to an empty dictionary
|
91
|
+
arguments = {}
|
92
|
+
try:
|
93
|
+
arguments = json.loads(tool_call.toolCall.function.arguments)
|
94
|
+
except Exception:
|
95
|
+
pass
|
96
|
+
try:
|
97
|
+
response = tool(**arguments)
|
98
|
+
except Exception as e:
|
99
|
+
error_message = (
|
100
|
+
"Error occurred while running tool" + f"{tool.__name__}: {e}"
|
101
|
+
)
|
102
|
+
e = ToolCallError(error=error_message, reqId=req_id)
|
103
|
+
websocket.send(e.model_dump_json())
|
104
|
+
continue
|
105
|
+
formatted_response = None
|
106
|
+
try:
|
107
|
+
formatted_response = ToolCallResponse(
|
108
|
+
reqId=tool_call.reqId, response=response
|
113
109
|
)
|
110
|
+
except pydantic.ValidationError:
|
111
|
+
formatted_response = ToolCallResponse(
|
112
|
+
reqId=tool_call.reqId, response=str(response)
|
113
|
+
)
|
114
|
+
websocket.send(formatted_response.model_dump_json())
|
114
115
|
websocket.send(
|
115
116
|
DeregisterDebuggerRequest(
|
116
|
-
debuggerSessionId=self.session,
|
117
|
-
deregister=True
|
117
|
+
debuggerSessionId=self.session, deregister=True
|
118
118
|
).model_dump_json()
|
119
119
|
)
|
120
120
|
|
@@ -122,10 +122,8 @@ class RemoteDebugger:
|
|
122
122
|
return uuid.uuid4().urn[9:]
|
123
123
|
|
124
124
|
def _format_session_id_and_registerd_functions(self) -> str:
|
125
|
-
registered_functions =
|
126
|
-
|
127
|
-
return \
|
128
|
-
f"""
|
125
|
+
registered_functions = ",\n".join(["- " + k for k in self.tools.keys()])
|
126
|
+
return f"""
|
129
127
|
========================================
|
130
128
|
Debugger Session ID:
|
131
129
|
{self.session}
|
@@ -136,5 +134,3 @@ Registered functions:
|
|
136
134
|
|
137
135
|
========================================
|
138
136
|
"""
|
139
|
-
|
140
|
-
|
lmnr/types.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import requests
|
2
2
|
import pydantic
|
3
3
|
import uuid
|
4
|
-
from typing import Union, Optional
|
4
|
+
from typing import Callable, Union, Optional
|
5
5
|
|
6
6
|
|
7
7
|
class ChatMessage(pydantic.BaseModel):
|
@@ -90,3 +90,12 @@ class RegisterDebuggerRequest(pydantic.BaseModel):
|
|
90
90
|
class DeregisterDebuggerRequest(pydantic.BaseModel):
|
91
91
|
debuggerSessionId: str
|
92
92
|
deregister: bool
|
93
|
+
|
94
|
+
|
95
|
+
class NodeFunction:
|
96
|
+
node_name: str
|
97
|
+
function: Callable[..., NodeInput]
|
98
|
+
|
99
|
+
def __init__(self, node_name: str, function: Callable[..., NodeInput]):
|
100
|
+
self.node_name = node_name
|
101
|
+
self.function = function
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: lmnr
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.10
|
4
4
|
Summary: Python SDK for Laminar AI
|
5
5
|
License: Apache-2.0
|
6
6
|
Author: lmnr.ai
|
@@ -14,7 +14,9 @@ Classifier: Programming Language :: Python :: 3.12
|
|
14
14
|
Requires-Dist: black (>=24.4.2,<25.0.0)
|
15
15
|
Requires-Dist: click (>=8.1.7,<9.0.0)
|
16
16
|
Requires-Dist: cookiecutter (>=2.6.0,<3.0.0)
|
17
|
+
Requires-Dist: lmnr-baml (>=0.40.0,<0.41.0)
|
17
18
|
Requires-Dist: pydantic (>=2.7.4,<3.0.0)
|
19
|
+
Requires-Dist: pystache (>=0.6.5,<0.7.0)
|
18
20
|
Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
|
19
21
|
Requires-Dist: requests (>=2.32.3,<3.0.0)
|
20
22
|
Requires-Dist: websockets (>=12.0,<13.0)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
lmnr/__init__.py,sha256=BBJ87AiHC_OpaYOCzF8QSsf7eO3LlJPOCBXHNKurbE4,235
|
2
|
+
lmnr/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
+
lmnr/cli/__main__.py,sha256=8hDtWlaFZK24KhfNq_ZKgtXqYHsDQDetukOCMlsbW0Q,59
|
4
|
+
lmnr/cli/cli.py,sha256=qG3-aY2EJIW3BSD-_s1vUsxHcxC9kiuIeAO8aVUq3ik,4103
|
5
|
+
lmnr/cli/parser/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
|
+
lmnr/cli/parser/nodes/__init__.py,sha256=2MkPdKulb1kuNe6aT71CaqBA8iBrXyb5pq5bu_EvCb8,1052
|
7
|
+
lmnr/cli/parser/nodes/code.py,sha256=8lTPBibUzaw_t-9QoPljhxH3KA4CLn9DJjA-iWpprOA,933
|
8
|
+
lmnr/cli/parser/nodes/condition.py,sha256=AJny0ILXbSy1hTwsRvZvDUqts9INNx63yQSkD7Dp7KU,740
|
9
|
+
lmnr/cli/parser/nodes/input.py,sha256=o8EfCmBbNyQL8FzmAtgnNDFlWqZmRAgkbw4HzKXZepU,539
|
10
|
+
lmnr/cli/parser/nodes/json_extractor.py,sha256=CnVwZ-wU_Ro4WkJLw9Uk_SS3yvZ66UPa5mK4JdkM8w4,723
|
11
|
+
lmnr/cli/parser/nodes/llm.py,sha256=Wpmo9cfNiYN9DRbj7oBS6RYcKXLwlGtF6RdF4jFQm5I,1866
|
12
|
+
lmnr/cli/parser/nodes/output.py,sha256=1XBppSscxM01kfZhE9oOh2GgdCVzyPVe2RAxLI5HmUc,665
|
13
|
+
lmnr/cli/parser/nodes/router.py,sha256=dmCx4ho8_GdFJXQa8UevMf_uEP7AKBv_MJ2zpLC6Vck,894
|
14
|
+
lmnr/cli/parser/nodes/semantic_search.py,sha256=DWDPpV78XZ7vPIaPd86FbeDFAnKah4e61M1TOzwnt84,1352
|
15
|
+
lmnr/cli/parser/nodes/types.py,sha256=OVXj-iMEDY9nPKCX1-zddtoszZcUL3CXYYryI7O3et0,6094
|
16
|
+
lmnr/cli/parser/parser.py,sha256=yDa-ysAkh6si_hHU8Gw8EdtNWc4pFc5RbvgWEXGEPys,2370
|
17
|
+
lmnr/cli/parser/utils.py,sha256=1oy6BApHXOF7BTXbP8v3Oi9bwOdWZjoxDlRIOfXVxro,1169
|
18
|
+
lmnr/cli/zip.py,sha256=DpAfSVg6k0Ly55SY7rsIZkX0XG6U6IPD-HzsNi5Cf-U,445
|
19
|
+
lmnr/sdk/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
20
|
+
lmnr/sdk/endpoint.py,sha256=0HjcxMUcJz-klFZO2f5xtTaoLjcaEb8vrJ_YldTWUc8,7467
|
21
|
+
lmnr/sdk/registry.py,sha256=sEYQFOjO72YvgBSEkBrvoewFExoyBzx6nELgBarvD6Y,755
|
22
|
+
lmnr/sdk/remote_debugger.py,sha256=BYAN13KUDxH412qD3HXdDH0RfokrePquDt35fzB7GUg,5010
|
23
|
+
lmnr/types.py,sha256=3HpLBQZr6F5YMISHYLnzzyrTwUttNqJxpyobw31YYJQ,2347
|
24
|
+
lmnr-0.2.10.dist-info/LICENSE,sha256=67b_wJHVV1CBaWkrKFWU1wyqTPSdzH77Ls-59631COg,10411
|
25
|
+
lmnr-0.2.10.dist-info/METADATA,sha256=STv0WzrtahyFBUaq_uLDuPVts5pd3dLgCfjdeQJ0Oyk,5565
|
26
|
+
lmnr-0.2.10.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
27
|
+
lmnr-0.2.10.dist-info/entry_points.txt,sha256=Qg7ZRax4k-rcQsZ26XRYQ8YFSBiyY2PNxYfq4a6PYXI,41
|
28
|
+
lmnr-0.2.10.dist-info/RECORD,,
|
lmnr-0.2.8.dist-info/RECORD
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
lmnr/__init__.py,sha256=NWHDZ-KAl3pQGDnKUbBbZaW4es-0-9Xt6gP-UrjyuEQ,207
|
2
|
-
lmnr/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
|
-
lmnr/cli/__main__.py,sha256=8hDtWlaFZK24KhfNq_ZKgtXqYHsDQDetukOCMlsbW0Q,59
|
4
|
-
lmnr/cli/cli.py,sha256=0Qw_dxE_N9F38asUB7pMbILJGVi-pPtqiao4aTjQQGM,2769
|
5
|
-
lmnr/cli/parser/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
|
-
lmnr/cli/parser/nodes/__init__.py,sha256=BNbbfn0WwbFDA6TNhLOaT_Ji69rCL5voUibqMD7Knng,1163
|
7
|
-
lmnr/cli/parser/nodes/code.py,sha256=GXqOxN6tdiStZGWLbN3WZCmDfzwYIgSRmZ5t72AOIXc,661
|
8
|
-
lmnr/cli/parser/nodes/condition.py,sha256=AJny0ILXbSy1hTwsRvZvDUqts9INNx63yQSkD7Dp7KU,740
|
9
|
-
lmnr/cli/parser/nodes/input.py,sha256=Xwktcih7Mezqv4cEejfPkpG8uJxDsbqRytBvKmlJDYE,578
|
10
|
-
lmnr/cli/parser/nodes/llm.py,sha256=iQWYFnQi5PcQD9WJpTSHbSzClM6s0wBOoEqyN5c4yQo,1674
|
11
|
-
lmnr/cli/parser/nodes/output.py,sha256=1XBppSscxM01kfZhE9oOh2GgdCVzyPVe2RAxLI5HmUc,665
|
12
|
-
lmnr/cli/parser/nodes/router.py,sha256=dmCx4ho8_GdFJXQa8UevMf_uEP7AKBv_MJ2zpLC6Vck,894
|
13
|
-
lmnr/cli/parser/nodes/semantic_search.py,sha256=DWDPpV78XZ7vPIaPd86FbeDFAnKah4e61M1TOzwnt84,1352
|
14
|
-
lmnr/cli/parser/nodes/types.py,sha256=XJQUg5VTbpc8LKN3-DMZkDMDiweGtKlA05edTcMYbr0,5257
|
15
|
-
lmnr/cli/parser/parser.py,sha256=kAZEeg358lyj_Q1IIhQB_bA7LW3Aw6RduShIfUSmLqQ,2173
|
16
|
-
lmnr/cli/parser/utils.py,sha256=wVaqHVOR9VXl8Og9nkVyCVgHIcgbtYGkDOEGPtmjZ8g,715
|
17
|
-
lmnr/sdk/endpoint.py,sha256=0HjcxMUcJz-klFZO2f5xtTaoLjcaEb8vrJ_YldTWUc8,7467
|
18
|
-
lmnr/sdk/remote_debugger.py,sha256=tC8OywEkxzeUCAHrpfhlL-iMP_0Rqh9qC2DtoYEGJjU,5174
|
19
|
-
lmnr/types.py,sha256=OR9xRAQ5uTTwpJTDL_e3jZqxYJWvyX96CCoxr3oo94g,2112
|
20
|
-
lmnr-0.2.8.dist-info/LICENSE,sha256=67b_wJHVV1CBaWkrKFWU1wyqTPSdzH77Ls-59631COg,10411
|
21
|
-
lmnr-0.2.8.dist-info/METADATA,sha256=kfHTeQtkFVsk3JK3LopVuvfr5OQkjG0YGXyoRrrqPq4,5479
|
22
|
-
lmnr-0.2.8.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
23
|
-
lmnr-0.2.8.dist-info/entry_points.txt,sha256=Qg7ZRax4k-rcQsZ26XRYQ8YFSBiyY2PNxYfq4a6PYXI,41
|
24
|
-
lmnr-0.2.8.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|