nebu 0.1.114__py3-none-any.whl → 0.1.115__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.
- nebu/processors/decorate.py +57 -4
- nebu/processors/processor.py +93 -60
- {nebu-0.1.114.dist-info → nebu-0.1.115.dist-info}/METADATA +1 -1
- {nebu-0.1.114.dist-info → nebu-0.1.115.dist-info}/RECORD +7 -7
- {nebu-0.1.114.dist-info → nebu-0.1.115.dist-info}/WHEEL +0 -0
- {nebu-0.1.114.dist-info → nebu-0.1.115.dist-info}/licenses/LICENSE +0 -0
- {nebu-0.1.114.dist-info → nebu-0.1.115.dist-info}/top_level.txt +0 -0
nebu/processors/decorate.py
CHANGED
@@ -1194,6 +1194,52 @@ def processor(
|
|
1194
1194
|
|
1195
1195
|
# --- Final Setup ---
|
1196
1196
|
logger.debug("Decorator: Preparing final Processor object...")
|
1197
|
+
|
1198
|
+
# Determine ResolvedInputType for Processor Generic
|
1199
|
+
ResolvedInputType: type[BaseModel] = (
|
1200
|
+
BaseModel # Default to BaseModel to satisfy generic bound
|
1201
|
+
)
|
1202
|
+
if is_stream_message:
|
1203
|
+
if (
|
1204
|
+
content_type
|
1205
|
+
and isinstance(content_type, type)
|
1206
|
+
and issubclass(content_type, BaseModel)
|
1207
|
+
):
|
1208
|
+
ResolvedInputType = content_type
|
1209
|
+
else:
|
1210
|
+
logger.warning(
|
1211
|
+
f"Decorator: Message type hint found, but ContentType '{content_type!s}' is not a valid Pydantic Model. Defaulting InputType to BaseModel."
|
1212
|
+
)
|
1213
|
+
# ResolvedInputType remains BaseModel (default)
|
1214
|
+
elif (
|
1215
|
+
param_type
|
1216
|
+
and isinstance(param_type, type)
|
1217
|
+
and issubclass(param_type, BaseModel)
|
1218
|
+
):
|
1219
|
+
ResolvedInputType = param_type # Function takes the data model directly
|
1220
|
+
else:
|
1221
|
+
logger.warning(
|
1222
|
+
f"Decorator: Parameter type '{param_type!s}' is not a valid Pydantic Model. Defaulting InputType to BaseModel."
|
1223
|
+
)
|
1224
|
+
# ResolvedInputType remains BaseModel (default)
|
1225
|
+
|
1226
|
+
ResolvedOutputType: type[BaseModel] = BaseModel # Default to BaseModel
|
1227
|
+
if (
|
1228
|
+
return_type
|
1229
|
+
and isinstance(return_type, type)
|
1230
|
+
and issubclass(return_type, BaseModel)
|
1231
|
+
):
|
1232
|
+
ResolvedOutputType = return_type
|
1233
|
+
elif return_type is not None: # It was something, but not a BaseModel subclass
|
1234
|
+
logger.warning(
|
1235
|
+
f"Decorator: Return type '{return_type!s}' is not a valid Pydantic Model. Defaulting OutputType to BaseModel."
|
1236
|
+
)
|
1237
|
+
# Else (return_type is None), ResolvedOutputType remains BaseModel
|
1238
|
+
|
1239
|
+
logger.debug(
|
1240
|
+
f"Decorator: Resolved Generic Types for Processor: InputType={ResolvedInputType.__name__}, OutputType={ResolvedOutputType.__name__}"
|
1241
|
+
)
|
1242
|
+
|
1197
1243
|
metadata = V1ResourceMetaRequest(
|
1198
1244
|
name=processor_name, namespace=effective_namespace, labels=labels
|
1199
1245
|
)
|
@@ -1225,7 +1271,7 @@ def processor(
|
|
1225
1271
|
final_command = "\n".join(all_commands)
|
1226
1272
|
|
1227
1273
|
logger.debug(
|
1228
|
-
f"Decorator: Final container command
|
1274
|
+
f"Decorator: Final container command:\\n-------\\n{final_command}\\n-------"
|
1229
1275
|
)
|
1230
1276
|
|
1231
1277
|
container_request = V1ContainerRequest(
|
@@ -1259,21 +1305,28 @@ def processor(
|
|
1259
1305
|
else:
|
1260
1306
|
logger.debug(f" {env_var.key}: {value_str}")
|
1261
1307
|
|
1262
|
-
|
1308
|
+
# Create the generically typed Processor instance
|
1309
|
+
_processor_instance = Processor[ResolvedInputType, ResolvedOutputType](
|
1263
1310
|
name=processor_name,
|
1264
1311
|
namespace=effective_namespace,
|
1265
1312
|
labels=labels,
|
1266
1313
|
container=container_request,
|
1267
|
-
|
1314
|
+
input_model_cls=ResolvedInputType,
|
1315
|
+
output_model_cls=ResolvedOutputType,
|
1268
1316
|
common_schema=None,
|
1269
1317
|
min_replicas=min_replicas,
|
1270
1318
|
max_replicas=max_replicas,
|
1271
1319
|
scale_config=scale,
|
1320
|
+
config=effective_config,
|
1321
|
+
api_key=api_key,
|
1272
1322
|
no_delete=no_delete,
|
1273
1323
|
wait_for_healthy=wait_for_healthy,
|
1274
1324
|
)
|
1325
|
+
# Type hint for the variable. The instance itself IS correctly typed with specific models.
|
1326
|
+
processor_instance: Processor[BaseModel, BaseModel] = _processor_instance
|
1327
|
+
|
1275
1328
|
logger.debug(
|
1276
|
-
f"Decorator: Processor instance '{processor_name}' created successfully."
|
1329
|
+
f"Decorator: Processor instance '{processor_name}' created successfully with generic types."
|
1277
1330
|
)
|
1278
1331
|
# Store original func for potential local invocation/testing? Keep for now.
|
1279
1332
|
# TODO: Add original_func to Processor model definition if this is desired
|
nebu/processors/processor.py
CHANGED
@@ -2,7 +2,17 @@ import json
|
|
2
2
|
import threading
|
3
3
|
import time
|
4
4
|
import uuid
|
5
|
-
from typing import
|
5
|
+
from typing import (
|
6
|
+
Any,
|
7
|
+
Dict,
|
8
|
+
Generic,
|
9
|
+
List,
|
10
|
+
Optional,
|
11
|
+
TypeVar,
|
12
|
+
cast,
|
13
|
+
get_args,
|
14
|
+
get_origin,
|
15
|
+
)
|
6
16
|
|
7
17
|
import requests
|
8
18
|
from pydantic import BaseModel
|
@@ -101,7 +111,8 @@ class Processor(Generic[InputType, OutputType]):
|
|
101
111
|
namespace: Optional[str] = None,
|
102
112
|
labels: Optional[Dict[str, str]] = None,
|
103
113
|
container: Optional[V1ContainerRequest] = None,
|
104
|
-
|
114
|
+
input_model_cls: Optional[type[BaseModel]] = None,
|
115
|
+
output_model_cls: Optional[type[BaseModel]] = None,
|
105
116
|
common_schema: Optional[str] = None,
|
106
117
|
min_replicas: Optional[int] = None,
|
107
118
|
max_replicas: Optional[int] = None,
|
@@ -124,7 +135,8 @@ class Processor(Generic[InputType, OutputType]):
|
|
124
135
|
self.namespace = namespace
|
125
136
|
self.labels = labels
|
126
137
|
self.container = container
|
127
|
-
self.
|
138
|
+
self.input_model_cls = input_model_cls
|
139
|
+
self.output_model_cls = output_model_cls
|
128
140
|
self.common_schema = common_schema
|
129
141
|
self.min_replicas = min_replicas
|
130
142
|
self.max_replicas = max_replicas
|
@@ -132,37 +144,26 @@ class Processor(Generic[InputType, OutputType]):
|
|
132
144
|
self.processors_url = f"{self.orign_host}/v1/processors"
|
133
145
|
self._log_thread: Optional[threading.Thread] = None
|
134
146
|
|
135
|
-
#
|
136
|
-
|
137
|
-
print("self.__dict__: ", self.__dict__)
|
138
|
-
if self.schema_ is None and hasattr(self, "__orig_class__"):
|
147
|
+
# Infer OutputType Pydantic class if output_model_cls is not provided
|
148
|
+
if self.output_model_cls is None and hasattr(self, "__orig_class__"):
|
139
149
|
type_args = get_args(self.__orig_class__) # type: ignore
|
140
|
-
print(">>> type_args: ", type_args)
|
141
150
|
if len(type_args) == 2:
|
142
151
|
output_type_candidate = type_args[1]
|
143
|
-
print(">>> output_type_candidate: ", output_type_candidate)
|
144
|
-
# Check if it looks like a Pydantic model class
|
145
152
|
if isinstance(output_type_candidate, type) and issubclass(
|
146
153
|
output_type_candidate, BaseModel
|
147
154
|
):
|
148
|
-
print(">>> output_type_candidate is a Pydantic model class")
|
149
155
|
logger.debug(
|
150
|
-
f"Inferred
|
156
|
+
f"Inferred output_model_cls {output_type_candidate.__name__} from generic arguments."
|
151
157
|
)
|
152
|
-
self.
|
158
|
+
self.output_model_cls = output_type_candidate
|
153
159
|
else:
|
154
|
-
print(">>> output_type_candidate is not a Pydantic model class")
|
155
160
|
logger.debug(
|
156
161
|
f"Second generic argument {output_type_candidate} is not a Pydantic BaseModel. "
|
157
|
-
"Cannot infer
|
162
|
+
"Cannot infer output_model_cls."
|
158
163
|
)
|
159
164
|
else:
|
160
|
-
print(
|
161
|
-
"Could not infer OutputType from generic arguments: wrong number of type args found "
|
162
|
-
f"(expected 2, got {len(type_args) if type_args else 0})."
|
163
|
-
)
|
164
165
|
logger.debug(
|
165
|
-
"Could not infer
|
166
|
+
"Could not infer output_model_cls from generic arguments: wrong number of type args found "
|
166
167
|
f"(expected 2, got {len(type_args) if type_args else 0})."
|
167
168
|
)
|
168
169
|
|
@@ -201,7 +202,6 @@ class Processor(Generic[InputType, OutputType]):
|
|
201
202
|
processor_request = V1ProcessorRequest(
|
202
203
|
metadata=metadata,
|
203
204
|
container=container,
|
204
|
-
schema_=schema_,
|
205
205
|
common_schema=common_schema,
|
206
206
|
min_replicas=min_replicas,
|
207
207
|
max_replicas=max_replicas,
|
@@ -226,7 +226,6 @@ class Processor(Generic[InputType, OutputType]):
|
|
226
226
|
|
227
227
|
update_processor = V1UpdateProcessor(
|
228
228
|
container=container,
|
229
|
-
schema_=schema_,
|
230
229
|
common_schema=common_schema,
|
231
230
|
min_replicas=min_replicas,
|
232
231
|
max_replicas=max_replicas,
|
@@ -341,38 +340,37 @@ class Processor(Generic[InputType, OutputType]):
|
|
341
340
|
|
342
341
|
# Attempt to parse into OutputType if conditions are met
|
343
342
|
print(f">>> wait: {wait}")
|
344
|
-
print(f">>> self.
|
345
|
-
print(">>> type(self.
|
346
|
-
print(
|
343
|
+
print(f">>> self.output_model_cls: {self.output_model_cls}")
|
344
|
+
print(">>> type(self.output_model_cls): ", type(self.output_model_cls))
|
345
|
+
print(
|
346
|
+
f">>> isinstance(self.output_model_cls, type): {isinstance(self.output_model_cls, type)}"
|
347
|
+
)
|
347
348
|
print(f">>> isinstance(raw_content, dict): {isinstance(raw_content, dict)}")
|
348
349
|
if (
|
349
350
|
wait
|
350
|
-
and self.
|
351
|
-
and isinstance(self.
|
352
|
-
and issubclass(self.
|
351
|
+
and self.output_model_cls
|
352
|
+
and isinstance(self.output_model_cls, type)
|
353
|
+
and issubclass(self.output_model_cls, BaseModel) # type: ignore
|
353
354
|
and isinstance(raw_content, dict)
|
354
|
-
):
|
355
|
+
):
|
355
356
|
print(f">>> raw_content: {raw_content}")
|
356
357
|
try:
|
357
|
-
|
358
|
-
# Parse raw_content instead of the full response
|
359
|
-
parsed_model = self.schema_.model_validate(raw_content)
|
358
|
+
parsed_model = self.output_model_cls.model_validate(raw_content)
|
360
359
|
print(f">>> parsed_model: {parsed_model}")
|
361
|
-
# Cast to OutputType to satisfy the linter with generics
|
362
360
|
parsed_output: OutputType = cast(OutputType, parsed_model)
|
363
361
|
print(f">>> parsed_output: {parsed_output}")
|
364
362
|
return parsed_output
|
365
|
-
except
|
366
|
-
Exception
|
367
|
-
) as e: # Consider pydantic.ValidationError for more specific handling
|
363
|
+
except Exception as e:
|
368
364
|
print(f">>> error: {e}")
|
369
|
-
|
365
|
+
model_name = getattr(
|
366
|
+
self.output_model_cls, "__name__", str(self.output_model_cls)
|
367
|
+
)
|
370
368
|
logger.error(
|
371
|
-
f"Processor {processor_name}: Failed to parse 'content' field into output type {
|
369
|
+
f"Processor {processor_name}: Failed to parse 'content' field into output type {model_name}. "
|
372
370
|
f"Error: {e}. Returning raw JSON response."
|
373
371
|
)
|
374
|
-
# Fallback to returning the raw JSON response
|
375
372
|
return raw_content
|
373
|
+
# Fallback logic using self.schema_ has been removed.
|
376
374
|
|
377
375
|
return raw_content
|
378
376
|
|
@@ -401,6 +399,8 @@ class Processor(Generic[InputType, OutputType]):
|
|
401
399
|
namespace: Optional[str] = None,
|
402
400
|
config: Optional[GlobalConfig] = None,
|
403
401
|
api_key: Optional[str] = None,
|
402
|
+
input_model_cls: Optional[type[BaseModel]] = None,
|
403
|
+
output_model_cls: Optional[type[BaseModel]] = None,
|
404
404
|
):
|
405
405
|
"""
|
406
406
|
Get a Processor from the remote server.
|
@@ -412,27 +412,60 @@ class Processor(Generic[InputType, OutputType]):
|
|
412
412
|
raise ValueError("Processor not found")
|
413
413
|
processor_v1 = processors[0]
|
414
414
|
|
415
|
-
|
415
|
+
# Try to infer Input/Output model classes if Processor.load is called as generic
|
416
|
+
# e.g., MyProcessor = Processor[MyInput, MyOutput]; MyProcessor.load(...)
|
417
|
+
loaded_input_model_cls: Optional[type[BaseModel]] = None
|
418
|
+
loaded_output_model_cls: Optional[type[BaseModel]] = None
|
419
|
+
|
420
|
+
# __orig_bases__ usually contains the generic version of the class if it was parameterized.
|
421
|
+
# We look for Processor[...] in the bases.
|
422
|
+
if hasattr(cls, "__orig_bases__"):
|
423
|
+
for base in cls.__orig_bases__: # type: ignore
|
424
|
+
if get_origin(base) is Processor:
|
425
|
+
type_args = get_args(base)
|
426
|
+
if len(type_args) == 2:
|
427
|
+
input_arg, output_arg = type_args
|
428
|
+
if isinstance(input_arg, type) and issubclass(
|
429
|
+
input_arg, BaseModel
|
430
|
+
):
|
431
|
+
loaded_input_model_cls = input_arg
|
432
|
+
if isinstance(output_arg, type) and issubclass(
|
433
|
+
output_arg, BaseModel
|
434
|
+
):
|
435
|
+
loaded_output_model_cls = output_arg
|
436
|
+
break # Found Processor generic base
|
437
|
+
|
438
|
+
# Determine final model classes, prioritizing overrides
|
439
|
+
final_input_model_cls = (
|
440
|
+
input_model_cls if input_model_cls is not None else loaded_input_model_cls
|
441
|
+
)
|
442
|
+
final_output_model_cls = (
|
443
|
+
output_model_cls
|
444
|
+
if output_model_cls is not None
|
445
|
+
else loaded_output_model_cls
|
446
|
+
)
|
447
|
+
|
448
|
+
out = cls.__new__(cls) # type: ignore
|
449
|
+
# If generic types were successfully inferred or overridden, pass them to init
|
450
|
+
# Otherwise, they will be None, and __init__ might try __orig_class__ if called on instance
|
451
|
+
out.__init__( # type: ignore
|
452
|
+
name=processor_v1.metadata.name, # Use name from fetched metadata
|
453
|
+
namespace=processor_v1.metadata.namespace, # Use namespace from fetched metadata
|
454
|
+
labels=processor_v1.metadata.labels, # Use labels from fetched metadata
|
455
|
+
container=processor_v1.container,
|
456
|
+
input_model_cls=final_input_model_cls, # Use determined input model
|
457
|
+
output_model_cls=final_output_model_cls, # Use determined output model
|
458
|
+
common_schema=processor_v1.common_schema,
|
459
|
+
min_replicas=processor_v1.min_replicas,
|
460
|
+
max_replicas=processor_v1.max_replicas,
|
461
|
+
scale_config=processor_v1.scale,
|
462
|
+
config=config, # Pass original config
|
463
|
+
api_key=api_key, # Pass original api_key
|
464
|
+
)
|
465
|
+
# The __init__ call above handles most setup. We store the fetched processor data.
|
416
466
|
out.processor = processor_v1
|
417
|
-
|
418
|
-
|
419
|
-
raise ValueError("No config found")
|
420
|
-
out.current_server = out.config.get_current_server_config()
|
421
|
-
if not out.current_server:
|
422
|
-
raise ValueError("No server config found")
|
423
|
-
out.api_key = api_key or out.current_server.api_key
|
424
|
-
out.orign_host = out.current_server.server
|
425
|
-
out.processors_url = f"{out.orign_host}/v1/processors"
|
426
|
-
out.name = name
|
427
|
-
out.namespace = namespace
|
428
|
-
|
429
|
-
# Set specific fields from the processor
|
430
|
-
out.container = processor_v1.container
|
431
|
-
out.schema_ = processor_v1.schema_
|
432
|
-
out.common_schema = processor_v1.common_schema
|
433
|
-
out.min_replicas = processor_v1.min_replicas
|
434
|
-
out.max_replicas = processor_v1.max_replicas
|
435
|
-
out.scale_config = processor_v1.scale
|
467
|
+
# self.schema_ was removed, so no assignment for it here from processor_v1.schema_
|
468
|
+
# out.common_schema = processor_v1.common_schema # This is now set in __init__
|
436
469
|
|
437
470
|
return out
|
438
471
|
|
@@ -549,7 +582,7 @@ class Processor(Generic[InputType, OutputType]):
|
|
549
582
|
|
550
583
|
# Send health check and wait for response
|
551
584
|
response = self.send(
|
552
|
-
data=health_check_data, # type: ignore
|
585
|
+
data=health_check_data, # type: ignore[arg-type]
|
553
586
|
wait=True,
|
554
587
|
timeout=30.0, # Short timeout for individual health check
|
555
588
|
)
|
@@ -15,14 +15,14 @@ nebu/namespaces/models.py,sha256=EqUOpzhVBhvJw2P92ONDUbIgC31M9jMmcaG5vyOrsWg,497
|
|
15
15
|
nebu/namespaces/namespace.py,sha256=oeZyGqsIGIrppyjif1ZONsdTmqRgd9oSLFE1BChXTTE,5247
|
16
16
|
nebu/processors/consumer.py,sha256=9WapzBTPuXRunH-vjPerTlGZy__hn_d4m13l1ajebY8,62732
|
17
17
|
nebu/processors/consumer_process_worker.py,sha256=h--eNFKaLbUayxn88mB8oGGdrU2liE1dnwm_TPlewX8,36960
|
18
|
-
nebu/processors/decorate.py,sha256=
|
18
|
+
nebu/processors/decorate.py,sha256=wnos4w7htBUSOnNVyhY7NQgUuz9d2_-Kre5H99UiFpw,61089
|
19
19
|
nebu/processors/default.py,sha256=cy4ETMdbdRGkrvbYec1o60h7mGDlGN5JsuUph0ENtDU,364
|
20
20
|
nebu/processors/models.py,sha256=g4B1t6Rgoy-NUEHBLeQc0EENzHXLDlWSio8Muv7cTDU,4093
|
21
|
-
nebu/processors/processor.py,sha256=
|
21
|
+
nebu/processors/processor.py,sha256=fMXzodTe2qWnoCBry9nLsHLMEI6FxOOmcsR9DbuY_W0,24574
|
22
22
|
nebu/redis/models.py,sha256=coPovAcVXnOU1Xh_fpJL4PO3QctgK9nBe5QYoqEcnxg,1230
|
23
23
|
nebu/services/service.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
24
|
-
nebu-0.1.
|
25
|
-
nebu-0.1.
|
26
|
-
nebu-0.1.
|
27
|
-
nebu-0.1.
|
28
|
-
nebu-0.1.
|
24
|
+
nebu-0.1.115.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
25
|
+
nebu-0.1.115.dist-info/METADATA,sha256=tud697lMA-FHCANFaBZKT20hTCyIxRMMQT3ZNdc0tQk,1798
|
26
|
+
nebu-0.1.115.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
|
27
|
+
nebu-0.1.115.dist-info/top_level.txt,sha256=uLIbEKJeGSHWOAJN5S0i5XBGwybALlF9bYoB1UhdEgQ,5
|
28
|
+
nebu-0.1.115.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|