flare-kernel-client-py 0.1.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.
- flare_kernel_client_py-0.1.0/PKG-INFO +26 -0
- flare_kernel_client_py-0.1.0/README.md +14 -0
- flare_kernel_client_py-0.1.0/pyproject.toml +25 -0
- flare_kernel_client_py-0.1.0/setup.cfg +4 -0
- flare_kernel_client_py-0.1.0/src/flare_kernel_client/__init__.py +4 -0
- flare_kernel_client_py-0.1.0/src/flare_kernel_client/kernel_stream_adapter.py +77 -0
- flare_kernel_client_py-0.1.0/src/flare_kernel_client/sse_parser.py +47 -0
- flare_kernel_client_py-0.1.0/src/flare_kernel_client_py.egg-info/PKG-INFO +26 -0
- flare_kernel_client_py-0.1.0/src/flare_kernel_client_py.egg-info/SOURCES.txt +11 -0
- flare_kernel_client_py-0.1.0/src/flare_kernel_client_py.egg-info/dependency_links.txt +1 -0
- flare_kernel_client_py-0.1.0/src/flare_kernel_client_py.egg-info/requires.txt +1 -0
- flare_kernel_client_py-0.1.0/src/flare_kernel_client_py.egg-info/top_level.txt +1 -0
- flare_kernel_client_py-0.1.0/tests/test_basics.py +34 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: flare-kernel-client-py
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: FLARE kernel client package
|
|
5
|
+
License: Proprietary
|
|
6
|
+
Classifier: Development Status :: 3 - Alpha
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
9
|
+
Requires-Python: >=3.11
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
Requires-Dist: httpx<1.0,>=0.27
|
|
12
|
+
|
|
13
|
+
# flare-kernel-client-py
|
|
14
|
+
|
|
15
|
+
FLARE Kernel 流式客户端与 SSE 解析包。
|
|
16
|
+
|
|
17
|
+
职责:
|
|
18
|
+
|
|
19
|
+
1. 连接 Kernel SSE 端点。
|
|
20
|
+
2. 解析 `event:` / `data:` 行。
|
|
21
|
+
3. 不承载业务字段、不承载 UI。
|
|
22
|
+
|
|
23
|
+
导出:
|
|
24
|
+
|
|
25
|
+
1. `KernelStreamAdapter`
|
|
26
|
+
2. `SSELineParser`
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "flare-kernel-client-py"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "FLARE kernel client package"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
dependencies = [
|
|
12
|
+
"httpx>=0.27,<1.0",
|
|
13
|
+
]
|
|
14
|
+
license = { text = "Proprietary" }
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"Programming Language :: Python :: 3.11",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
[tool.setuptools]
|
|
22
|
+
package-dir = {"" = "src"}
|
|
23
|
+
|
|
24
|
+
[tool.setuptools.packages.find]
|
|
25
|
+
where = ["src"]
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from typing import Any, AsyncGenerator
|
|
5
|
+
|
|
6
|
+
from .sse_parser import SSELineParser
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _read_env(name: str) -> str | None:
|
|
10
|
+
value = os.getenv(name)
|
|
11
|
+
if value is None:
|
|
12
|
+
return None
|
|
13
|
+
normalized = value.strip()
|
|
14
|
+
return normalized or None
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _resolve_required_value(explicit: str | None, env_name: str) -> str:
|
|
18
|
+
candidate = (explicit or _read_env(env_name) or "").strip()
|
|
19
|
+
if not candidate:
|
|
20
|
+
raise ValueError(f"{env_name} is required for KernelStreamAdapter")
|
|
21
|
+
return candidate
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class KernelStreamAdapter:
|
|
25
|
+
"""Generic adapter for streaming events from a Kernel SSE endpoint."""
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
base_url: str | None = None,
|
|
30
|
+
path: str | None = None,
|
|
31
|
+
timeout: float = 60.0,
|
|
32
|
+
) -> None:
|
|
33
|
+
self.base_url = _resolve_required_value(base_url, "KERNEL_BASE_URL").rstrip("/")
|
|
34
|
+
self.path = _resolve_required_value(path, "KERNEL_STREAM_PATH")
|
|
35
|
+
self.timeout = timeout
|
|
36
|
+
|
|
37
|
+
def build_stream_url(self) -> str:
|
|
38
|
+
return f"{self.base_url}/{self.path.lstrip('/')}"
|
|
39
|
+
|
|
40
|
+
async def stream_chat(
|
|
41
|
+
self,
|
|
42
|
+
session_id: str,
|
|
43
|
+
message: str,
|
|
44
|
+
enabled_tools: list[str] | None = None,
|
|
45
|
+
) -> AsyncGenerator[dict[str, Any], None]:
|
|
46
|
+
import httpx
|
|
47
|
+
|
|
48
|
+
payload = {
|
|
49
|
+
"message": message,
|
|
50
|
+
"content": message,
|
|
51
|
+
"session_id": session_id,
|
|
52
|
+
"enabled_tools": enabled_tools or [],
|
|
53
|
+
"enabled_capabilities": enabled_tools or [],
|
|
54
|
+
}
|
|
55
|
+
parser = SSELineParser()
|
|
56
|
+
|
|
57
|
+
async with httpx.AsyncClient(timeout=self.timeout) as client:
|
|
58
|
+
async with client.stream(
|
|
59
|
+
"POST",
|
|
60
|
+
self.build_stream_url(),
|
|
61
|
+
json=payload,
|
|
62
|
+
headers={"Accept": "text/event-stream"},
|
|
63
|
+
) as response:
|
|
64
|
+
if response.status_code != 200:
|
|
65
|
+
error_text = await response.aread()
|
|
66
|
+
raise Exception(
|
|
67
|
+
f"Kernel returned error {response.status_code}: "
|
|
68
|
+
f"{error_text.decode()}"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
async for line in response.aiter_lines():
|
|
72
|
+
event = parser.feed_line(line)
|
|
73
|
+
if event is not None:
|
|
74
|
+
yield event
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
__all__ = ["KernelStreamAdapter"]
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class SSELineParser:
|
|
8
|
+
"""Minimal SSE text line parser.
|
|
9
|
+
|
|
10
|
+
It keeps only the current `event:` value and turns a matching `data:` line
|
|
11
|
+
into a plain dict: {"type": ..., "data": ...}.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
def __init__(self) -> None:
|
|
15
|
+
self._current_event: str | None = None
|
|
16
|
+
|
|
17
|
+
def reset(self) -> None:
|
|
18
|
+
self._current_event = None
|
|
19
|
+
|
|
20
|
+
def feed_line(self, line: str) -> dict[str, Any] | None:
|
|
21
|
+
if line is None:
|
|
22
|
+
return None
|
|
23
|
+
|
|
24
|
+
normalized = line.rstrip("\r")
|
|
25
|
+
if not normalized:
|
|
26
|
+
return None
|
|
27
|
+
|
|
28
|
+
if normalized.startswith("event:"):
|
|
29
|
+
self._current_event = normalized[6:].strip() or None
|
|
30
|
+
return None
|
|
31
|
+
|
|
32
|
+
if not normalized.startswith("data:") or not self._current_event:
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
raw_data = normalized[5:].strip()
|
|
36
|
+
if not raw_data:
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
try:
|
|
40
|
+
data = json.loads(raw_data)
|
|
41
|
+
except json.JSONDecodeError:
|
|
42
|
+
return None
|
|
43
|
+
|
|
44
|
+
return {"type": self._current_event, "data": data}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
__all__ = ["SSELineParser"]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: flare-kernel-client-py
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: FLARE kernel client package
|
|
5
|
+
License: Proprietary
|
|
6
|
+
Classifier: Development Status :: 3 - Alpha
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
9
|
+
Requires-Python: >=3.11
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
Requires-Dist: httpx<1.0,>=0.27
|
|
12
|
+
|
|
13
|
+
# flare-kernel-client-py
|
|
14
|
+
|
|
15
|
+
FLARE Kernel 流式客户端与 SSE 解析包。
|
|
16
|
+
|
|
17
|
+
职责:
|
|
18
|
+
|
|
19
|
+
1. 连接 Kernel SSE 端点。
|
|
20
|
+
2. 解析 `event:` / `data:` 行。
|
|
21
|
+
3. 不承载业务字段、不承载 UI。
|
|
22
|
+
|
|
23
|
+
导出:
|
|
24
|
+
|
|
25
|
+
1. `KernelStreamAdapter`
|
|
26
|
+
2. `SSELineParser`
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/flare_kernel_client/__init__.py
|
|
4
|
+
src/flare_kernel_client/kernel_stream_adapter.py
|
|
5
|
+
src/flare_kernel_client/sse_parser.py
|
|
6
|
+
src/flare_kernel_client_py.egg-info/PKG-INFO
|
|
7
|
+
src/flare_kernel_client_py.egg-info/SOURCES.txt
|
|
8
|
+
src/flare_kernel_client_py.egg-info/dependency_links.txt
|
|
9
|
+
src/flare_kernel_client_py.egg-info/requires.txt
|
|
10
|
+
src/flare_kernel_client_py.egg-info/top_level.txt
|
|
11
|
+
tests/test_basics.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
httpx<1.0,>=0.27
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
flare_kernel_client
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import flare_kernel_client
|
|
4
|
+
|
|
5
|
+
from flare_kernel_client import KernelStreamAdapter, SSELineParser
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_package_smoke_imports_public_api() -> None:
|
|
9
|
+
assert hasattr(flare_kernel_client, "KernelStreamAdapter")
|
|
10
|
+
assert hasattr(flare_kernel_client, "SSELineParser")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_sse_line_parser_parses_event_data_and_resets() -> None:
|
|
14
|
+
parser = SSELineParser()
|
|
15
|
+
|
|
16
|
+
assert parser.feed_line('data: {"ignored": true}') is None
|
|
17
|
+
assert parser.feed_line("event: message") is None
|
|
18
|
+
|
|
19
|
+
event = parser.feed_line('data: {"ok": true, "count": 2}')
|
|
20
|
+
assert event == {"type": "message", "data": {"ok": True, "count": 2}}
|
|
21
|
+
|
|
22
|
+
parser.reset()
|
|
23
|
+
assert parser.feed_line('data: {"ok": true}') is None
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_kernel_stream_adapter_build_stream_url_normalizes_slashes() -> None:
|
|
27
|
+
adapter = KernelStreamAdapter(
|
|
28
|
+
base_url="https://example.test/",
|
|
29
|
+
path="/kernel/stream",
|
|
30
|
+
timeout=12.5,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
assert adapter.build_stream_url() == "https://example.test/kernel/stream"
|
|
34
|
+
assert adapter.timeout == 12.5
|