jac-scale 0.1.1__py3-none-any.whl → 0.1.3__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.
@@ -7,7 +7,8 @@ class AppConfig {
7
7
  file_name: str = 'none',
8
8
  build: bool = False,
9
9
  app_name: (str | None) = None,
10
- testing: bool = False;
10
+ testing: bool = False,
11
+ experimental: bool = False;
11
12
 
12
13
  def init(
13
14
  self: AppConfig,
@@ -15,13 +16,15 @@ class AppConfig {
15
16
  file_name: str = 'none',
16
17
  build: bool = False,
17
18
  app_name: (str | None) = None,
18
- testing: bool = False
19
+ testing: bool = False,
20
+ experimental: bool = False
19
21
  ) -> None {
20
22
  self.code_folder = code_folder;
21
23
  self.file_name = file_name;
22
24
  self.build = build;
23
25
  self.app_name = app_name;
24
26
  self.testing = testing;
27
+ self.experimental = experimental;
25
28
  }
26
29
 
27
30
  def get_code_path(self: AppConfig) -> Path {
jac_scale/context.jac CHANGED
@@ -1,8 +1,9 @@
1
+ import from contextvars { ContextVar }
1
2
  import from dataclasses { MISSING }
2
3
  import from typing { Any }
3
4
  import from uuid { UUID }
4
5
  import from jaclang.pycore.constant { Constants as Con }
5
- import from jaclang.runtimelib.context { ExecutionContext }
6
+ import from jaclang.runtimelib.context { ExecutionContext, CallState }
6
7
 
7
8
  """Jac Scale Execution Context with custom memory backend.
8
9
 
@@ -0,0 +1,75 @@
1
+ """Factory for creating storage instances."""
2
+ import from typing { Any }
3
+ import os;
4
+ import from jaclang.runtimelib.storage { Storage }
5
+ import from jaclang.project.config { get_config }
6
+
7
+ enum StorageType {
8
+ LOCAL = "local"
9
+ # Future: S3, GCS, AZURE - add when implemented
10
+ }
11
+
12
+ """Factory for creating storage instances.
13
+
14
+ Configuration priority: jac.toml > environment variable > default
15
+
16
+ In jac.toml:
17
+ [storage]
18
+ type = "local" # or "s3", "gcs", "azure"
19
+ base_path = "/data/storage"
20
+ create_dirs = true
21
+
22
+ Environment variables:
23
+ JAC_STORAGE_TYPE: Storage type (local, s3, gcs, azure)
24
+ JAC_STORAGE_PATH: Base directory for local storage
25
+ JAC_STORAGE_CREATE_DIRS: Whether to auto-create directories
26
+ """
27
+ class StorageFactory {
28
+ static def create(
29
+ storage_type: (StorageType | str), config: (dict[str, Any] | None) = None
30
+ ) -> Storage {
31
+ # Convert string to enum if needed
32
+ if isinstance(storage_type, str) {
33
+ storage_type = StorageType(storage_type);
34
+ }
35
+
36
+ if storage_type == StorageType.LOCAL {
37
+ import from jaclang.runtimelib.storage { LocalStorage }
38
+ cfg = config or {};
39
+ return LocalStorage(
40
+ base_path=cfg.get("base_path", "./storage"),
41
+ create_dirs=cfg.get("create_dirs", True)
42
+ );
43
+ }
44
+ raise ValueError(f"Unsupported storage type: {storage_type}") ;
45
+ }
46
+
47
+ static def get_default(
48
+ base_path: str = "./storage", create_dirs: bool = True
49
+ ) -> Storage {
50
+ # Get config from jac.toml if available
51
+ jac_config = get_config();
52
+
53
+ # Config priority: jac.toml > env var > parameter
54
+ if jac_config and jac_config.storage {
55
+ storage_type = StorageType(jac_config.storage.storage_type);
56
+ base_path = jac_config.storage.base_path;
57
+ create_dirs = jac_config.storage.create_dirs;
58
+ } elif os.environ.get("JAC_STORAGE_TYPE") or os.environ.get("JAC_STORAGE_PATH") {
59
+ storage_type_str = os.environ.get(
60
+ "JAC_STORAGE_TYPE", StorageType.LOCAL.value
61
+ );
62
+ storage_type = StorageType(storage_type_str);
63
+ base_path = os.environ.get("JAC_STORAGE_PATH", base_path);
64
+ create_dirs_str = os.environ.get(
65
+ "JAC_STORAGE_CREATE_DIRS", str(create_dirs)
66
+ );
67
+ create_dirs = create_dirs_str.lower() == "true";
68
+ } else {
69
+ storage_type = StorageType.LOCAL;
70
+ }
71
+
72
+ storage_config = {"base_path": base_path, "create_dirs": create_dirs};
73
+ return StorageFactory.create(storage_type, storage_config);
74
+ }
75
+ }
@@ -0,0 +1,85 @@
1
+ """Google SSO Provider implementation for jac-scale.
2
+
3
+ This module implements the SSOProvider interface for Google OAuth authentication.
4
+ """
5
+
6
+ import from fastapi { Request, Response }
7
+ import from fastapi_sso.sso.google { GoogleSSO }
8
+ import from jac_scale.sso_provider { SSOProvider, SSOUserInfo }
9
+
10
+ """Google OAuth SSO Provider.
11
+
12
+ This class wraps the fastapi_sso GoogleSSO implementation to conform to
13
+ our SSOProvider interface, enabling consistent handling across all SSO vendors.
14
+ """
15
+ obj GoogleSSOProvider(SSOProvider) {
16
+ has client_id: str,
17
+ client_secret: str,
18
+ redirect_uri: str,
19
+ allow_insecure_http: bool = True,
20
+ _google_sso: (GoogleSSO | None) = None;
21
+
22
+ """Initialize the Google SSO provider."""
23
+ def postinit -> None {
24
+ self._google_sso = GoogleSSO(
25
+ client_id=self.client_id,
26
+ client_secret=self.client_secret,
27
+ redirect_uri=self.redirect_uri,
28
+ allow_insecure_http=self.allow_insecure_http
29
+ );
30
+ }
31
+
32
+ """Initiate Google OAuth authentication flow.
33
+
34
+ Args:
35
+ operation: The operation type ('login' or 'register')
36
+
37
+ Returns:
38
+ RedirectResponse to Google's OAuth authorization page
39
+ """
40
+ async def initiate_auth(operation: str) -> Response {
41
+ if not self._google_sso {
42
+ raise RuntimeError("GoogleSSO not initialized") ;
43
+ }
44
+
45
+ with self._google_sso {
46
+ return await self._google_sso.get_login_redirect();
47
+ }
48
+ }
49
+
50
+ """Handle the OAuth callback from Google.
51
+
52
+ Args:
53
+ request: The FastAPI request object containing OAuth callback data
54
+
55
+ Returns:
56
+ SSOUserInfo: Standardized user information from Google
57
+
58
+ Raises:
59
+ Exception: If authentication fails or user info cannot be retrieved
60
+ """
61
+ async def handle_callback(request: Request) -> SSOUserInfo {
62
+ if not self._google_sso {
63
+ raise RuntimeError("GoogleSSO not initialized") ;
64
+ }
65
+
66
+ with self._google_sso {
67
+ user_info = await self._google_sso.verify_and_process(request);
68
+ return SSOUserInfo(
69
+ email=user_info.email,
70
+ external_id=user_info.id,
71
+ platform=self.get_platform_name(),
72
+ display_name=user_info?.display_name
73
+ );
74
+ }
75
+ }
76
+
77
+ """Get the platform identifier.
78
+
79
+ Returns:
80
+ str: 'google'
81
+ """
82
+ def get_platform_name -> str {
83
+ return "google";
84
+ }
85
+ }
@@ -21,4 +21,7 @@ impl JScaleExecutionContext.init(self: JScaleExecutionContext) -> None {
21
21
  # Default user_root and entry_node to system_root
22
22
  self.user_root = self.system_root;
23
23
  self.entry_node = self.system_root;
24
+ self.call_state: ContextVar[CallState] = ContextVar(
25
+ 'call_state', default=CallState()
26
+ );
24
27
  }
@@ -486,10 +486,17 @@ impl JacAPIServer.render_base_route_callback(
486
486
 
487
487
  impl JacAPIServer.register_functions_endpoints -> None {
488
488
  for func_name in self.get_functions() {
489
+ func_obj = self.get_functions()[func_name];
490
+ restspec = func_obj.restspec if func_obj?.restspec else None;
491
+ spec_method = restspec.method if restspec else HTTPMethod.POST;
492
+ spec_path = restspec.path if restspec else None;
493
+
494
+ final_path = spec_path or f"/function/{func_name}";
495
+
489
496
  self.server.add_endpoint(
490
497
  JEndPoint(
491
- method=HTTPMethod.POST,
492
- path=f"/function/{func_name}",
498
+ method=spec_method,
499
+ path=final_path,
493
500
  callback=self.create_function_callback(func_name),
494
501
  parameters=self.create_function_parameters(func_name),
495
502
  response_model=None,
@@ -546,7 +553,7 @@ impl JacAPIServer.create_function_callback(
546
553
  ) -> Callable[..., TransportResponse] {
547
554
  import from jaclang.runtimelib.transport { TransportResponse, Meta }
548
555
  requires_auth = self.introspector.is_auth_required_for_function(func_name);
549
- def callback(**kwargs: JsonValue) -> TransportResponse {
556
+ async def callback(**kwargs: JsonValue) -> TransportResponse {
550
557
  username: (str | None) = None;
551
558
  if requires_auth {
552
559
  authorization = kwargs.pop('Authorization', None);
@@ -558,7 +565,7 @@ impl JacAPIServer.create_function_callback(
558
565
  ) {
559
566
  token = authorization[7:];
560
567
  }
561
- username = self.validate_jwt_token(token) if token else None;
568
+ username = self.user_manager.validate_jwt_token(token) if token else None;
562
569
  if not username {
563
570
  return TransportResponse.fail(
564
571
  code='UNAUTHORIZED',
@@ -568,9 +575,21 @@ impl JacAPIServer.create_function_callback(
568
575
  }
569
576
  }
570
577
  print(f"Executing function '{func_name}' with params: {kwargs}");
571
- result = self.execution_manager.execute_function(
578
+ result = await self.execution_manager.execute_function(
572
579
  self.get_functions()[func_name], kwargs, (username or '__guest__')
573
580
  );
581
+ # Handle streaming responses (generators/async generators)
582
+ if (isgenerator(result) or isinstance(result, AsyncGenerator)) {
583
+ return StreamingResponse(
584
+ result,
585
+ media_type='text/event-stream',
586
+ headers={
587
+ 'Cache-Control': 'no-cache',
588
+ 'Connection': 'close',
589
+ 'X-Accel-Buffering': 'no'
590
+ }
591
+ );
592
+ }
574
593
  if 'error' in result {
575
594
  return TransportResponse.fail(
576
595
  code='EXECUTION_ERROR',
@@ -588,10 +607,15 @@ impl JacAPIServer.create_function_callback(
588
607
 
589
608
  impl JacAPIServer.register_walkers_endpoints -> None {
590
609
  for walker_name in self.get_walkers() {
610
+ walker_cls = self.get_walkers()[walker_name];
611
+ restspec = walker_cls.restspec if walker_cls?.restspec else None;
612
+ spec_method = restspec.method if restspec?.method else HTTPMethod.POST;
613
+ spec_path = restspec.path if restspec?.path else f"/walker/{walker_name}";
614
+
591
615
  self.server.add_endpoint(
592
616
  JEndPoint(
593
- method=HTTPMethod.POST,
594
- path=f"/walker/{walker_name}/{{node}}",
617
+ method=spec_method,
618
+ path=f"{spec_path}/{{node}}",
595
619
  callback=self.create_walker_callback(walker_name, has_node_param=True),
596
620
  parameters=self.create_walker_parameters(
597
621
  walker_name, invoke_on_root=False
@@ -604,8 +628,8 @@ impl JacAPIServer.register_walkers_endpoints -> None {
604
628
  );
605
629
  self.server.add_endpoint(
606
630
  JEndPoint(
607
- method=HTTPMethod.POST,
608
- path=f"/walker/{walker_name}",
631
+ method=spec_method,
632
+ path=spec_path,
609
633
  callback=self.create_walker_callback(walker_name, has_node_param=False),
610
634
  parameters=self.create_walker_parameters(
611
635
  walker_name, invoke_on_root=True
@@ -685,7 +709,7 @@ impl JacAPIServer.create_walker_callback(
685
709
  ) {
686
710
  token = authorization[7:];
687
711
  }
688
- username = self.validate_jwt_token(token) if token else None;
712
+ username = self.user_manager.validate_jwt_token(token) if token else None;
689
713
  if not username {
690
714
  return TransportResponse.fail(
691
715
  code='UNAUTHORIZED',
@@ -700,6 +724,17 @@ impl JacAPIServer.create_walker_callback(
700
724
  result = await self.execution_manager.spawn_walker(
701
725
  self.get_walkers()[walker_name], kwargs, (username or '__guest__')
702
726
  );
727
+ if (isgenerator(result) or isinstance(result, AsyncGenerator)) {
728
+ return StreamingResponse(
729
+ result,
730
+ media_type='text/event-stream',
731
+ headers={
732
+ 'Cache-Control': 'no-cache',
733
+ 'Connection': 'close',
734
+ 'X-Accel-Buffering': 'no'
735
+ }
736
+ );
737
+ }
703
738
  if 'error' in result {
704
739
  return TransportResponse.fail(
705
740
  code='EXECUTION_ERROR',
@@ -783,7 +818,7 @@ impl JacAPIServer.refresh_token(token: (str | None) = None) -> TransportResponse
783
818
  if token.startswith('Bearer ') {
784
819
  token = token[7:];
785
820
  }
786
- new_token = self.refresh_jwt_token(token);
821
+ new_token = self.user_manager.refresh_jwt_token(token);
787
822
  if (not new_token) {
788
823
  return TransportResponse.fail(
789
824
  code='UNAUTHORIZED',
@@ -809,7 +844,7 @@ impl JacAPIServer.create_user(username: str, password: str) -> TransportResponse
809
844
  meta=Meta(extra={'http_status': 400})
810
845
  );
811
846
  }
812
- res['token'] = self.create_jwt_token(username);
847
+ res['token'] = self.user_manager.create_jwt_token(username);
813
848
  return TransportResponse.success(
814
849
  data=res, meta=Meta(extra={'http_status': 201})
815
850
  );
@@ -837,7 +872,7 @@ impl JacAPIServer.update_username(
837
872
  ) {
838
873
  token = Authorization[7:];
839
874
  }
840
- token_username = self.validate_jwt_token(token) if token else None;
875
+ token_username = self.user_manager.validate_jwt_token(token) if token else None;
841
876
  if not token_username {
842
877
  return TransportResponse.fail(
843
878
  code='UNAUTHORIZED',
@@ -869,7 +904,7 @@ impl JacAPIServer.update_username(
869
904
  );
870
905
  }
871
906
  # Generate new JWT token with updated username
872
- result['token'] = self.create_jwt_token(new_username);
907
+ result['token'] = self.user_manager.create_jwt_token(new_username);
873
908
  return TransportResponse.success(
874
909
  data=result, meta=Meta(extra={'http_status': 200})
875
910
  );
@@ -891,7 +926,7 @@ impl JacAPIServer.update_password(
891
926
  ) {
892
927
  token = Authorization[7:];
893
928
  }
894
- token_username = self.validate_jwt_token(token) if token else None;
929
+ token_username = self.user_manager.validate_jwt_token(token) if token else None;
895
930
  if not token_username {
896
931
  return TransportResponse.fail(
897
932
  code='UNAUTHORIZED',
@@ -1066,7 +1101,7 @@ impl JacAPIServer.login(username: str, password: str) -> TransportResponse {
1066
1101
  meta=Meta(extra={'http_status': 401})
1067
1102
  );
1068
1103
  }
1069
- result['token'] = self.create_jwt_token(username);
1104
+ result['token'] = self.user_manager.create_jwt_token(username);
1070
1105
  return TransportResponse.success(
1071
1106
  data=dict[(str, JsonValue)](result), meta=Meta(extra={'http_status': 200})
1072
1107
  );
@@ -1109,219 +1144,6 @@ impl JacAPIServer.postinit -> None {
1109
1144
  }
1110
1145
  self.server.app.add_middleware(CustomHeadersMiddleware);
1111
1146
  }
1112
- self.SUPPORTED_PLATFORMS: dict = {};
1113
- # Load SSO config fresh (not from cached global) to support env var overrides at runtime
1114
- sso_config = get_scale_config().get_sso_config();
1115
- for platform in Platforms {
1116
- key = platform.lower();
1117
- platform_config = sso_config.get(key, {});
1118
-
1119
- client_id = platform_config.get('client_id', '');
1120
- client_secret = platform_config.get('client_secret', '');
1121
-
1122
- if not client_id or not client_secret {
1123
- continue;
1124
- }
1125
-
1126
- self.SUPPORTED_PLATFORMS[platform.value] = {
1127
- "client_id": client_id,
1128
- "client_secret": client_secret
1129
- };
1130
- }
1131
- }
1132
-
1133
- impl JacAPIServer.refresh_jwt_token(token: str) -> (str | None) {
1134
- try {
1135
- decoded = jwt.decode(
1136
- token, JWT_SECRET, algorithms=[JWT_ALGORITHM], options={"verify_exp": True}
1137
- );
1138
- username = decoded.get('username');
1139
-
1140
- if not username {
1141
- return None;
1142
- }
1143
-
1144
- return JacAPIServer.create_jwt_token(username);
1145
- } except Exception {
1146
- return None;
1147
- }
1148
- }
1149
-
1150
- impl JacAPIServer.validate_jwt_token(token: str) -> (str | None) {
1151
- try {
1152
- decoded = jwt.decode(token, JWT_SECRET, algorithms=[JWT_ALGORITHM]);
1153
- return decoded['username'];
1154
- } except Exception {
1155
- return None;
1156
- }
1157
- }
1158
-
1159
- impl JacAPIServer.create_jwt_token(username: str) -> str {
1160
- now = datetime.now(UTC);
1161
- payload: dict[(str, Any)] = {
1162
- 'username': username,
1163
- 'exp': (now + timedelta(days=JWT_EXP_DELTA_DAYS)),
1164
- 'iat': now.timestamp()
1165
- };
1166
- return jwt.encode(payload, JWT_SECRET, algorithm=JWT_ALGORITHM);
1167
- }
1168
-
1169
- impl JacAPIServer.get_sso(platform: str, operation: str) -> (GoogleSSO | None) {
1170
- if (platform not in self.SUPPORTED_PLATFORMS) {
1171
- return None;
1172
- }
1173
- credentials = self.SUPPORTED_PLATFORMS[platform];
1174
- redirect_uri = f"{SSO_HOST}/{platform}/{operation}/callback";
1175
- if (platform == Platforms.GOOGLE.value) {
1176
- return GoogleSSO(
1177
- client_id=credentials['client_id'],
1178
- client_secret=credentials['client_secret'],
1179
- redirect_uri=redirect_uri,
1180
- allow_insecure_http=True
1181
- );
1182
- }
1183
- return None;
1184
- }
1185
-
1186
- impl JacAPIServer.sso_initiate(
1187
- platform: str, operation: str
1188
- ) -> (Response | TransportResponse) {
1189
- import from jaclang.runtimelib.transport { TransportResponse, Meta }
1190
- if (platform not in [p.value for p in Platforms]) {
1191
- return TransportResponse.fail(
1192
- code='INVALID_PLATFORM',
1193
- message=f"Invalid platform '{platform}'. Supported platforms: {', '.join(
1194
- [p.value for p in Platforms]
1195
- )}",
1196
- meta=Meta(extra={'http_status': 400})
1197
- );
1198
- }
1199
- if (platform not in self.SUPPORTED_PLATFORMS) {
1200
- return TransportResponse.fail(
1201
- code='SSO_NOT_CONFIGURED',
1202
- message=f"SSO for platform '{platform}' is not configured. Please set SSO_{platform.upper()}_CLIENT_ID and SSO_{platform.upper()}_CLIENT_SECRET environment variables.",
1203
- meta=Meta(extra={'http_status': 501})
1204
- );
1205
- }
1206
- if (operation not in [o.value for o in Operations]) {
1207
- return TransportResponse.fail(
1208
- code='INVALID_OPERATION',
1209
- message=f"Invalid operation '{operation}'. Must be 'login' or 'register'",
1210
- meta=Meta(extra={'http_status': 400})
1211
- );
1212
- }
1213
- sso = self.get_sso(platform, operation);
1214
- if not sso {
1215
- return TransportResponse.fail(
1216
- code='SSO_INIT_FAILED',
1217
- message=f"Failed to initialize SSO for platform '{platform}'",
1218
- meta=Meta(extra={'http_status': 500})
1219
- );
1220
- }
1221
- with sso {
1222
- return await sso.get_login_redirect();
1223
- }
1224
- }
1225
-
1226
- impl JacAPIServer.sso_callback(
1227
- request: Request, platform: str, operation: str
1228
- ) -> TransportResponse {
1229
- import from jaclang.runtimelib.transport { TransportResponse, Meta }
1230
- if (platform not in [p.value for p in Platforms]) {
1231
- return TransportResponse.fail(
1232
- code='INVALID_PLATFORM',
1233
- message=f"Invalid platform '{platform}'. Supported platforms: {', '.join(
1234
- [p.value for p in Platforms]
1235
- )}",
1236
- meta=Meta(extra={'http_status': 400})
1237
- );
1238
- }
1239
- if (platform not in self.SUPPORTED_PLATFORMS) {
1240
- return TransportResponse.fail(
1241
- code='SSO_NOT_CONFIGURED',
1242
- message=f"SSO for platform '{platform}' is not configured. Please set SSO_{platform.upper()}_CLIENT_ID and SSO_{platform.upper()}_CLIENT_SECRET environment variables.",
1243
- meta=Meta(extra={'http_status': 501})
1244
- );
1245
- }
1246
- if (operation not in [o.value for o in Operations]) {
1247
- return TransportResponse.fail(
1248
- code='INVALID_OPERATION',
1249
- message=f"Invalid operation '{operation}'. Must be 'login' or 'register'",
1250
- meta=Meta(extra={'http_status': 400})
1251
- );
1252
- }
1253
- sso = self.get_sso(platform, operation);
1254
- if not sso {
1255
- return TransportResponse.fail(
1256
- code='SSO_INIT_FAILED',
1257
- message=f"Failed to initialize SSO for platform '{platform}'",
1258
- meta=Meta(extra={'http_status': 500})
1259
- );
1260
- }
1261
- try {
1262
- with sso {
1263
- user_info = await sso.verify_and_process(request);
1264
- email = user_info.email;
1265
- if not email {
1266
- return TransportResponse.fail(
1267
- code='EMAIL_MISSING',
1268
- message=f"Email not provided by {platform}",
1269
- meta=Meta(extra={'http_status': 400})
1270
- );
1271
- }
1272
- if (operation == Operations.LOGIN.value) {
1273
- user = self.user_manager.get_user(email);
1274
- if not user {
1275
- return TransportResponse.fail(
1276
- code='USER_NOT_FOUND',
1277
- message='User not found. Please register first.',
1278
- meta=Meta(extra={'http_status': 404})
1279
- );
1280
- }
1281
- token = self.create_jwt_token(email);
1282
- return TransportResponse.success(
1283
- data={
1284
- 'message': 'Login successful',
1285
- 'email': email,
1286
- 'token': token,
1287
- 'platform': platform,
1288
- 'user': dict[(str, JsonValue)](user)
1289
- },
1290
- meta=Meta(extra={'http_status': 200})
1291
- );
1292
- } elif (operation == Operations.REGISTER.value) {
1293
- existing_user = self.user_manager.get_user(email);
1294
- if existing_user {
1295
- return TransportResponse.fail(
1296
- code='USER_EXISTS',
1297
- message='User already exists. Please login instead.',
1298
- meta=Meta(extra={'http_status': 400})
1299
- );
1300
- }
1301
- random_password = generate_random_password();
1302
- result = self.user_manager.create_user(email, random_password);
1303
- if ('error' in result) {
1304
- return TransportResponse.fail(
1305
- code='USER_CREATION_FAILED',
1306
- message=result.get('error', 'User creation failed'),
1307
- meta=Meta(extra={'http_status': 400})
1308
- );
1309
- }
1310
- token = self.create_jwt_token(email);
1311
- result['token'] = token;
1312
- result['platform'] = platform;
1313
- return TransportResponse.success(
1314
- data=result, meta=Meta(extra={'http_status': 201})
1315
- );
1316
- }
1317
- }
1318
- } except Exception as e {
1319
- return TransportResponse.fail(
1320
- code='AUTHENTICATION_FAILED',
1321
- message=f"Authentication failed: {str(e)}",
1322
- meta=Meta(extra={'http_status': 500})
1323
- );
1324
- }
1325
1147
  }
1326
1148
 
1327
1149
  impl JacAPIServer.register_sso_endpoints -> None {
@@ -1329,7 +1151,7 @@ impl JacAPIServer.register_sso_endpoints -> None {
1329
1151
  JEndPoint(
1330
1152
  method=HTTPMethod.GET,
1331
1153
  path='/sso/{platform}/{operation}',
1332
- callback=self.sso_initiate,
1154
+ callback=self.user_manager.sso_initiate,
1333
1155
  parameters=[
1334
1156
  APIParameter(
1335
1157
  name='platform',
@@ -1358,7 +1180,7 @@ impl JacAPIServer.register_sso_endpoints -> None {
1358
1180
  JEndPoint(
1359
1181
  method=HTTPMethod.GET,
1360
1182
  path='/sso/{platform}/{operation}/callback',
1361
- callback=self.sso_callback,
1183
+ callback=self.user_manager.sso_callback,
1362
1184
  parameters=[
1363
1185
  APIParameter(
1364
1186
  name='platform',
@@ -1455,7 +1277,7 @@ impl JacAPIServer.register_dynamic_walker_endpoint -> None {
1455
1277
  ) {
1456
1278
  token = Authorization[7:];
1457
1279
  }
1458
- username = self.validate_jwt_token(token) if token else None;
1280
+ username = self.user_manager.validate_jwt_token(token) if token else None;
1459
1281
  if not username {
1460
1282
  return TransportResponse.fail(
1461
1283
  code='UNAUTHORIZED',
@@ -1473,6 +1295,20 @@ impl JacAPIServer.register_dynamic_walker_endpoint -> None {
1473
1295
  result = await self.execution_manager.spawn_walker(
1474
1296
  walkers[walker_name], kwargs, (username or '__guest__')
1475
1297
  );
1298
+
1299
+ # Handle streaming responses (generators/async generators)
1300
+ if (isgenerator(result) or isinstance(result, AsyncGenerator)) {
1301
+ return StreamingResponse(
1302
+ result,
1303
+ media_type='text/event-stream',
1304
+ headers={
1305
+ 'Cache-Control': 'no-cache',
1306
+ 'Connection': 'close',
1307
+ 'X-Accel-Buffering': 'no'
1308
+ }
1309
+ );
1310
+ }
1311
+
1476
1312
  if 'error' in result {
1477
1313
  return TransportResponse.fail(
1478
1314
  code='EXECUTION_ERROR',
@@ -1604,7 +1440,7 @@ impl JacAPIServer.register_dynamic_function_endpoint -> None {
1604
1440
  ) {
1605
1441
  token = Authorization[7:];
1606
1442
  }
1607
- username = self.validate_jwt_token(token) if token else None;
1443
+ username = self.user_manager.validate_jwt_token(token) if token else None;
1608
1444
  if not username {
1609
1445
  return TransportResponse.fail(
1610
1446
  code='UNAUTHORIZED',
@@ -1614,9 +1450,21 @@ impl JacAPIServer.register_dynamic_function_endpoint -> None {
1614
1450
  }
1615
1451
  }
1616
1452
 
1617
- result = self.execution_manager.execute_function(
1453
+ result = await self.execution_manager.execute_function(
1618
1454
  functions[function_name], kwargs, (username or '__guest__')
1619
1455
  );
1456
+ # Handle streaming responses (generators/async generators)
1457
+ if (isgenerator(result) or isinstance(result, AsyncGenerator)) {
1458
+ return StreamingResponse(
1459
+ result,
1460
+ media_type='text/event-stream',
1461
+ headers={
1462
+ 'Cache-Control': 'no-cache',
1463
+ 'Connection': 'close',
1464
+ 'X-Accel-Buffering': 'no'
1465
+ }
1466
+ );
1467
+ }
1620
1468
  if 'error' in result {
1621
1469
  return TransportResponse.fail(
1622
1470
  code='EXECUTION_ERROR',