Skip to content

Commit 0ae62d8

Browse files
committed
fix serve module merge artifacts
1 parent b7a013a commit 0ae62d8

4 files changed

Lines changed: 73 additions & 42 deletions

File tree

gliner/serve/__main__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,12 @@ def main():
152152
default="/gliner",
153153
help="HTTP route prefix",
154154
)
155+
server_group.add_argument(
156+
"--port",
157+
type=int,
158+
default=8000,
159+
help="HTTP port for Ray Serve",
160+
)
155161
server_group.add_argument(
156162
"--ray-address",
157163
type=str,
@@ -243,6 +249,7 @@ def main():
243249
target_memory_fraction=args.target_memory_fraction,
244250
memory_overhead_factor=args.memory_overhead_factor,
245251
warmup_iterations=args.warmup_iterations,
252+
http_port=args.port,
246253
ray_address=args.ray_address,
247254
)
248255

@@ -256,6 +263,7 @@ def main():
256263
print(f"Max batch size: {args.max_batch_size}") # noqa: T201
257264
print(f"Precompiled batch sizes: {precompiled_sizes}") # noqa: T201
258265
print(f"Num replicas: {config.num_replicas}") # noqa: T201
266+
print(f"Port: {args.port}") # noqa: T201
259267
print(f"Route prefix: {args.route_prefix}") # noqa: T201
260268
print(f"Compilation: {'enabled' if not args.no_compile else 'disabled'}") # noqa: T201
261269
print(f"FlashDeBERTa: {'enabled' if args.enable_flashdeberta else 'disabled'}") # noqa: T201

gliner/serve/client.py

Lines changed: 60 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""HTTP client for the GLiNER Ray Serve deployment.
1+
"""HTTP client for the GLiNER Ray Serve deployment."""
22

33
from typing import Any, Dict, List, Union, Optional
44

@@ -39,13 +39,52 @@ def __init__(
3939
max_concurrency: Maximum in-flight HTTP requests when predicting
4040
on a list of texts. Bounds the client-side thread pool.
4141
"""
42-
import ray # noqa: PLC0415
43-
from ray import serve # noqa: PLC0415
42+
self.url = base_url.rstrip("/") + route_prefix
43+
self.timeout = timeout
44+
self.max_concurrency = max_concurrency
4445

45-
if not ray.is_initialized():
46-
ray.init(address=ray_address, ignore_reinit_error=True)
47-
48-
self._handle = serve.get_deployment_handle(deployment_name, "gliner")
46+
def _build_payload(
47+
self,
48+
text: str,
49+
labels: List[str],
50+
relations: Optional[List[str]],
51+
threshold: Optional[float],
52+
relation_threshold: Optional[float],
53+
flat_ner: bool,
54+
multi_label: bool,
55+
) -> Dict[str, Any]:
56+
"""Build the JSON payload for a single prediction request."""
57+
payload: Dict[str, Any] = {
58+
"text": text,
59+
"labels": labels,
60+
"flat_ner": flat_ner,
61+
"multi_label": multi_label,
62+
}
63+
if relations is not None:
64+
payload["relations"] = relations
65+
if threshold is not None:
66+
payload["threshold"] = threshold
67+
if relation_threshold is not None:
68+
payload["relation_threshold"] = relation_threshold
69+
return payload
70+
71+
def _post(self, payload: Dict[str, Any]) -> Dict[str, Any]:
72+
"""Send a single POST request to the server."""
73+
import json # noqa: PLC0415
74+
import urllib.request # noqa: PLC0415
75+
76+
data = json.dumps(payload).encode()
77+
req = urllib.request.Request(
78+
self.url,
79+
data=data,
80+
headers={"Content-Type": "application/json"},
81+
method="POST",
82+
)
83+
try:
84+
with urllib.request.urlopen(req, timeout=self.timeout) as resp:
85+
return json.loads(resp.read())
86+
except Exception as exc:
87+
raise GLiNERClientError(f"Request to {self.url} failed: {exc}") from exc
4988

5089
def predict(
5190
self,
@@ -57,7 +96,7 @@ def predict(
5796
flat_ner: bool = True,
5897
multi_label: bool = False,
5998
) -> Union[Dict[str, Any], List[Dict[str, Any]]]:
60-
"""Blocking prediction. ``str`` in ``dict`` out; ``list`` in ``list`` out."""
99+
"""Blocking prediction. ``str`` in -> ``dict`` out; ``list`` in -> ``list`` out."""
61100
single = isinstance(text, str)
62101
items = [text] if single else list(text)
63102

@@ -69,27 +108,11 @@ def predict(
69108
for t in items
70109
]
71110

72-
Args:
73-
text: Input text or list of texts.
74-
labels: Entity type labels to extract.
75-
relations: Relation type labels (for relex models).
76-
threshold: Confidence threshold for entities.
77-
relation_threshold: Confidence threshold for relations.
78-
flat_ner: Whether to use flat NER.
79-
multi_label: Whether to allow multiple labels per span.
80-
81-
Returns:
82-
Single result dict or list of result dicts containing:
83-
- "entities": List of entity dicts
84-
- "relations": List of relation dicts (if model supports)
85-
"""
86-
if isinstance(text, list):
87-
refs = [
88-
self._handle.predict.remote(t, labels, relations, threshold, relation_threshold, flat_ner, multi_label)
89-
for t in text
90-
]
91-
return [ref.result() for ref in refs]
111+
if len(payloads) == 1:
112+
results = [self._post(payloads[0])]
92113
else:
114+
from concurrent.futures import ThreadPoolExecutor # noqa: PLC0415
115+
93116
workers = min(self.max_concurrency, len(payloads))
94117
with ThreadPoolExecutor(max_workers=workers) as pool:
95118
results = list(pool.map(self._post, payloads))
@@ -107,18 +130,15 @@ async def predict_async(
107130
multi_label: bool = False,
108131
) -> Union[Dict[str, Any], List[Dict[str, Any]]]:
109132
"""Async version of predict."""
110-
if isinstance(text, list):
111-
import asyncio # noqa: PLC0415
112-
113-
refs = [
114-
self._handle.predict.remote(t, labels, relations, threshold, relation_threshold, flat_ner, multi_label)
115-
for t in text
116-
]
117-
results = await asyncio.gather(*refs)
118-
return list(results)
119-
else:
120-
return await self._handle.predict.remote(
121-
text, labels, relations, threshold, relation_threshold, flat_ner, multi_label
133+
import asyncio # noqa: PLC0415
134+
135+
single = isinstance(text, str)
136+
items = [text] if single else list(text)
137+
138+
payloads = [
139+
self._build_payload(
140+
t, labels, relations, threshold, relation_threshold,
141+
flat_ner, multi_label,
122142
)
123143
for t in items
124144
]

gliner/serve/config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ class GLiNERServeConfig:
5454

5555
warmup_iterations: int = 3
5656

57+
http_port: int = 8000
58+
5759
ray_address: Optional[str] = None
5860

5961
def __post_init__(self):

gliner/serve/server.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -553,12 +553,12 @@ def serve(
553553
if not ray.is_initialized():
554554
ray.init(address=config.ray_address, ignore_reinit_error=True)
555555

556-
ray_serve.start(detached=True)
556+
ray_serve.start(detached=True, http_options={"port": config.http_port})
557557

558558
app = _build_deployment(config)
559559
handle = ray_serve.run(app, name="gliner", route_prefix=config.route_prefix)
560560

561-
logger.info("GLiNER server running at http://localhost:8000%s", config.route_prefix)
561+
logger.info("GLiNER server running at http://localhost:%d%s", config.http_port, config.route_prefix)
562562

563563
if blocking:
564564
import time # noqa: PLC0415
@@ -715,6 +715,7 @@ def shutdown(self) -> None:
715715
"""
716716
if self._closed:
717717
return
718+
import ray # noqa: PLC0415
718719
from ray import serve as ray_serve # noqa: PLC0415
719720

720721
ray_serve.shutdown()

0 commit comments

Comments
 (0)