npcpy 1.3.4__py3-none-any.whl → 1.3.6__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.
- npcpy/build_funcs.py +288 -0
- npcpy/data/load.py +1 -1
- npcpy/data/web.py +5 -4
- npcpy/gen/image_gen.py +2 -1
- npcpy/gen/response.py +119 -66
- npcpy/gen/world_gen.py +609 -0
- npcpy/llm_funcs.py +177 -271
- npcpy/memory/command_history.py +107 -2
- npcpy/memory/knowledge_graph.py +1 -1
- npcpy/npc_compiler.py +176 -32
- npcpy/npc_sysenv.py +5 -5
- npcpy/serve.py +311 -2
- npcpy/sql/npcsql.py +272 -59
- npcpy/work/browser.py +30 -0
- {npcpy-1.3.4.dist-info → npcpy-1.3.6.dist-info}/METADATA +1 -1
- {npcpy-1.3.4.dist-info → npcpy-1.3.6.dist-info}/RECORD +19 -16
- {npcpy-1.3.4.dist-info → npcpy-1.3.6.dist-info}/WHEEL +0 -0
- {npcpy-1.3.4.dist-info → npcpy-1.3.6.dist-info}/licenses/LICENSE +0 -0
- {npcpy-1.3.4.dist-info → npcpy-1.3.6.dist-info}/top_level.txt +0 -0
npcpy/sql/npcsql.py
CHANGED
|
@@ -7,6 +7,12 @@ from collections import defaultdict, deque
|
|
|
7
7
|
from sqlalchemy import create_engine, text, Engine, inspect
|
|
8
8
|
import inspect as py_inspect
|
|
9
9
|
|
|
10
|
+
try:
|
|
11
|
+
from jinja2 import Environment, BaseLoader, DebugUndefined
|
|
12
|
+
JINJA_AVAILABLE = True
|
|
13
|
+
except ImportError:
|
|
14
|
+
JINJA_AVAILABLE = False
|
|
15
|
+
|
|
10
16
|
# --- Explicitly import llm_funcs as a module object ---
|
|
11
17
|
try:
|
|
12
18
|
import npcpy.llm_funcs as llm_funcs
|
|
@@ -74,10 +80,8 @@ class DatabaseAIFunctionMapper:
|
|
|
74
80
|
'cortex_function': 'COMPLETE',
|
|
75
81
|
'transformer': lambda prompt, **kwargs: f"SNOWFLAKE.CORTEX.COMPLETE('llama3.1-8b', {prompt})"
|
|
76
82
|
},
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
'transformer': lambda text, **kwargs: f"SNOWFLAKE.CORTEX.COMPLETE('llama3.1-8b', CONCAT('Extract concise facts from this text. Return JSON with fact_list array. Text: ', {text}))"
|
|
80
|
-
},
|
|
83
|
+
|
|
84
|
+
|
|
81
85
|
'get_facts': {
|
|
82
86
|
'cortex_function': 'COMPLETE',
|
|
83
87
|
'transformer': lambda text, **kwargs: f"""SNOWFLAKE.CORTEX.COMPLETE('llama3.1-8b',
|
|
@@ -189,42 +193,183 @@ class NativeDatabaseAITransformer:
|
|
|
189
193
|
|
|
190
194
|
return transformer(**kwargs)
|
|
191
195
|
|
|
196
|
+
|
|
197
|
+
# --- NQL Jinja Context ---
|
|
198
|
+
class NQLJinjaContext:
|
|
199
|
+
"""Provides Jinja template context for NQL models with access to NPCs, jinxs, and team."""
|
|
200
|
+
|
|
201
|
+
def __init__(self, team=None, npc_operations=None):
|
|
202
|
+
self.team = team
|
|
203
|
+
self.npc_operations = npc_operations
|
|
204
|
+
self._npc_cache = {}
|
|
205
|
+
self._jinx_cache = {}
|
|
206
|
+
|
|
207
|
+
def npc(self, name: str) -> dict:
|
|
208
|
+
"""Get NPC properties by name. Usage: {{ npc('sibiji').model }}"""
|
|
209
|
+
if name in self._npc_cache:
|
|
210
|
+
return self._npc_cache[name]
|
|
211
|
+
|
|
212
|
+
if not self.team:
|
|
213
|
+
return {'name': name, 'error': 'No team loaded'}
|
|
214
|
+
|
|
215
|
+
npc_obj = self.team.get_npc(name)
|
|
216
|
+
if not npc_obj:
|
|
217
|
+
return {'name': name, 'error': f'NPC {name} not found'}
|
|
218
|
+
|
|
219
|
+
# Build properties dict
|
|
220
|
+
props = {
|
|
221
|
+
'name': getattr(npc_obj, 'name', name),
|
|
222
|
+
'model': getattr(npc_obj, 'model', 'gpt-4o-mini'),
|
|
223
|
+
'provider': getattr(npc_obj, 'provider', 'openai'),
|
|
224
|
+
'directive': getattr(npc_obj, 'primary_directive', ''),
|
|
225
|
+
'jinxs': getattr(npc_obj, 'jinxs', []),
|
|
226
|
+
}
|
|
227
|
+
self._npc_cache[name] = props
|
|
228
|
+
return props
|
|
229
|
+
|
|
230
|
+
def jinx(self, name: str) -> dict:
|
|
231
|
+
"""Get jinx properties by name. Usage: {{ jinx('sample').description }}"""
|
|
232
|
+
if name in self._jinx_cache:
|
|
233
|
+
return self._jinx_cache[name]
|
|
234
|
+
|
|
235
|
+
if not self.npc_operations or not self.npc_operations.jinx_map:
|
|
236
|
+
return {'name': name, 'error': 'No jinxs loaded'}
|
|
237
|
+
|
|
238
|
+
jinx_info = self.npc_operations.jinx_map.get(name.lower())
|
|
239
|
+
if not jinx_info:
|
|
240
|
+
return {'name': name, 'error': f'Jinx {name} not found'}
|
|
241
|
+
|
|
242
|
+
props = {
|
|
243
|
+
'name': jinx_info.get('name', name),
|
|
244
|
+
'description': jinx_info.get('description', ''),
|
|
245
|
+
'inputs': jinx_info.get('inputs', []),
|
|
246
|
+
}
|
|
247
|
+
self._jinx_cache[name] = props
|
|
248
|
+
return props
|
|
249
|
+
|
|
250
|
+
def get_team_context(self) -> dict:
|
|
251
|
+
"""Get team-level properties. Usage: {{ team.forenpc }}"""
|
|
252
|
+
if not self.team:
|
|
253
|
+
return {'error': 'No team loaded'}
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
'name': getattr(self.team, 'name', 'npc_team'),
|
|
257
|
+
'forenpc': getattr(self.team, 'forenpc_name', None),
|
|
258
|
+
'npcs': [getattr(n, 'name', str(n)) for n in getattr(self.team, 'npcs', [])],
|
|
259
|
+
'jinx_count': len(self.npc_operations.jinx_map) if self.npc_operations else 0,
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
def ref(self, model_name: str) -> str:
|
|
263
|
+
"""Reference another model. Usage: {{ ref('base_stats') }}"""
|
|
264
|
+
return f"{{{{ ref('{model_name}') }}}}"
|
|
265
|
+
|
|
266
|
+
def config(self, **kwargs) -> str:
|
|
267
|
+
"""Model configuration. Usage: {{ config(materialized='table') }}"""
|
|
268
|
+
config_parts = [f"{k}='{v}'" if isinstance(v, str) else f"{k}={v}"
|
|
269
|
+
for k, v in kwargs.items()]
|
|
270
|
+
return f"{{{{ config({', '.join(config_parts)}) }}}}"
|
|
271
|
+
|
|
272
|
+
def env(self, var_name: str, default: str = '') -> str:
|
|
273
|
+
"""Get environment variable. Usage: {{ env('API_KEY') }}"""
|
|
274
|
+
return os.environ.get(var_name, default)
|
|
275
|
+
|
|
276
|
+
def build_jinja_env(self) -> 'Environment':
|
|
277
|
+
"""Build Jinja2 environment with NQL functions."""
|
|
278
|
+
if not JINJA_AVAILABLE:
|
|
279
|
+
raise ImportError("Jinja2 is required for template processing. Install with: pip install jinja2")
|
|
280
|
+
|
|
281
|
+
env = Environment(
|
|
282
|
+
loader=BaseLoader(),
|
|
283
|
+
undefined=DebugUndefined,
|
|
284
|
+
# Keep {{ ref(...) }} and {{ config(...) }} unprocessed for later
|
|
285
|
+
variable_start_string='{%',
|
|
286
|
+
variable_end_string='%}',
|
|
287
|
+
block_start_string='{%%',
|
|
288
|
+
block_end_string='%%}',
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
# Add custom functions
|
|
292
|
+
env.globals['npc'] = self.npc
|
|
293
|
+
env.globals['jinx'] = self.jinx
|
|
294
|
+
env.globals['team'] = self.get_team_context()
|
|
295
|
+
env.globals['env'] = self.env
|
|
296
|
+
|
|
297
|
+
return env
|
|
298
|
+
|
|
299
|
+
def render_template(self, content: str) -> str:
|
|
300
|
+
"""Render Jinja template in SQL content."""
|
|
301
|
+
if not JINJA_AVAILABLE:
|
|
302
|
+
return content
|
|
303
|
+
|
|
304
|
+
# Only process if there are NQL Jinja expressions ({% ... %})
|
|
305
|
+
if '{%' not in content:
|
|
306
|
+
return content
|
|
307
|
+
|
|
308
|
+
try:
|
|
309
|
+
env = self.build_jinja_env()
|
|
310
|
+
template = env.from_string(content)
|
|
311
|
+
return template.render()
|
|
312
|
+
except Exception as e:
|
|
313
|
+
print(f"Warning: Jinja template error: {e}")
|
|
314
|
+
return content
|
|
315
|
+
|
|
316
|
+
|
|
192
317
|
# --- NPCSQL Operations ---
|
|
193
318
|
class NPCSQLOperations:
|
|
194
319
|
def __init__(
|
|
195
|
-
self,
|
|
196
|
-
npc_directory: str,
|
|
320
|
+
self,
|
|
321
|
+
npc_directory: str,
|
|
197
322
|
db_engine: Union[str, Engine] = "~/npcsh_history.db"
|
|
198
323
|
):
|
|
199
324
|
self.npc_directory = npc_directory
|
|
200
|
-
|
|
325
|
+
|
|
201
326
|
if isinstance(db_engine, str):
|
|
202
327
|
self.engine = create_engine_from_path(db_engine)
|
|
203
328
|
else:
|
|
204
329
|
self.engine = db_engine
|
|
205
|
-
|
|
330
|
+
|
|
206
331
|
self.npc_loader = None
|
|
332
|
+
self.jinx_map = {} # Maps jinx names to jinx objects
|
|
207
333
|
self.function_map = self._build_function_map()
|
|
208
|
-
|
|
334
|
+
|
|
209
335
|
def _get_team(self):
|
|
210
|
-
return (self.npc_loader
|
|
211
|
-
if hasattr(self.npc_loader, 'npcs')
|
|
336
|
+
return (self.npc_loader
|
|
337
|
+
if hasattr(self.npc_loader, 'npcs')
|
|
212
338
|
else None)
|
|
213
339
|
|
|
214
340
|
def _build_function_map(self):
|
|
215
341
|
import types
|
|
216
|
-
|
|
342
|
+
|
|
217
343
|
function_map = {}
|
|
218
344
|
for name in dir(llm_funcs):
|
|
219
345
|
if name.startswith('_'):
|
|
220
346
|
continue
|
|
221
347
|
obj = getattr(llm_funcs, name)
|
|
222
|
-
if (isinstance(obj, types.FunctionType) or
|
|
348
|
+
if (isinstance(obj, types.FunctionType) or
|
|
223
349
|
(isinstance(obj, types.MethodType) and obj.__self__ is not None)):
|
|
224
350
|
function_map[name] = obj
|
|
225
|
-
|
|
351
|
+
|
|
226
352
|
return function_map
|
|
227
353
|
|
|
354
|
+
def load_team_jinxs(self, team):
|
|
355
|
+
"""Load jinxs from team to make them available as NQL functions."""
|
|
356
|
+
if not team:
|
|
357
|
+
return
|
|
358
|
+
|
|
359
|
+
try:
|
|
360
|
+
# Get all jinxs from the team's jinx catalog
|
|
361
|
+
if hasattr(team, 'jinx_tool_catalog'):
|
|
362
|
+
for tool in team.jinx_tool_catalog:
|
|
363
|
+
jinx_name = tool.get('name', '').lower()
|
|
364
|
+
if jinx_name and jinx_name not in self.function_map:
|
|
365
|
+
# Store reference to the jinx
|
|
366
|
+
self.jinx_map[jinx_name] = tool
|
|
367
|
+
# Add a placeholder to function_map so it's recognized
|
|
368
|
+
self.function_map[jinx_name] = f"__jinx__{jinx_name}"
|
|
369
|
+
print(f"NQL: Registered team jinx '{jinx_name}' as NQL function")
|
|
370
|
+
except Exception as e:
|
|
371
|
+
print(f"Warning: Could not load team jinxs: {e}")
|
|
372
|
+
|
|
228
373
|
def _resolve_npc_reference(self, npc_ref: str):
|
|
229
374
|
if not npc_ref or not self.npc_loader:
|
|
230
375
|
return None
|
|
@@ -252,6 +397,37 @@ class NPCSQLOperations:
|
|
|
252
397
|
|
|
253
398
|
return None
|
|
254
399
|
|
|
400
|
+
def _execute_jinx(self, jinx_name: str, query: str, npc_ref: str, context: str = "") -> str:
|
|
401
|
+
"""Execute a team jinx and return the result."""
|
|
402
|
+
try:
|
|
403
|
+
from npcpy.npc_compiler import execute_jinx
|
|
404
|
+
|
|
405
|
+
jinx_info = self.jinx_map.get(jinx_name)
|
|
406
|
+
if not jinx_info:
|
|
407
|
+
return f"Error: Jinx '{jinx_name}' not found"
|
|
408
|
+
|
|
409
|
+
# Build context for jinx execution
|
|
410
|
+
jinx_context = {
|
|
411
|
+
'input': query,
|
|
412
|
+
'prompt': query,
|
|
413
|
+
'text': query,
|
|
414
|
+
'context': context,
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
# Get the jinx object from team
|
|
418
|
+
team = self._get_team()
|
|
419
|
+
if team and hasattr(team, 'get_jinx'):
|
|
420
|
+
jinx = team.get_jinx(jinx_name)
|
|
421
|
+
if jinx:
|
|
422
|
+
result = execute_jinx(jinx, jinx_context, team=team)
|
|
423
|
+
if isinstance(result, dict):
|
|
424
|
+
return result.get('output', str(result))
|
|
425
|
+
return str(result)
|
|
426
|
+
|
|
427
|
+
return f"Error: Could not execute jinx '{jinx_name}'"
|
|
428
|
+
except Exception as e:
|
|
429
|
+
return f"Jinx error: {e}"
|
|
430
|
+
|
|
255
431
|
def execute_ai_function(
|
|
256
432
|
self,
|
|
257
433
|
func_name: str,
|
|
@@ -262,6 +438,7 @@ class NPCSQLOperations:
|
|
|
262
438
|
raise ValueError(f"Unknown AI function: {func_name}")
|
|
263
439
|
|
|
264
440
|
func = self.function_map[func_name]
|
|
441
|
+
is_jinx = isinstance(func, str) and func.startswith("__jinx__")
|
|
265
442
|
|
|
266
443
|
npc_ref = params.get('npc', '')
|
|
267
444
|
resolved_npc = self._resolve_npc_reference(npc_ref)
|
|
@@ -271,7 +448,8 @@ class NPCSQLOperations:
|
|
|
271
448
|
resolved_team = resolved_npc.team
|
|
272
449
|
|
|
273
450
|
total_rows = len(df)
|
|
274
|
-
|
|
451
|
+
func_type = "jinx" if is_jinx else "function"
|
|
452
|
+
print(f"NQL: Executing {func_type} '{func_name}' on {total_rows} rows with NPC '{npc_ref}'...")
|
|
275
453
|
|
|
276
454
|
results = []
|
|
277
455
|
for idx, (row_idx, row) in enumerate(df.iterrows()):
|
|
@@ -292,34 +470,45 @@ class NPCSQLOperations:
|
|
|
292
470
|
|
|
293
471
|
print(f" [{idx+1}/{total_rows}] Processing row {row_idx}...", end=" ", flush=True)
|
|
294
472
|
|
|
295
|
-
sig = py_inspect.signature(func)
|
|
296
|
-
|
|
297
|
-
# Extract model/provider from NPC if available
|
|
298
|
-
npc_model = None
|
|
299
|
-
npc_provider = None
|
|
300
|
-
if resolved_npc and hasattr(resolved_npc, 'model'):
|
|
301
|
-
npc_model = resolved_npc.model
|
|
302
|
-
if resolved_npc and hasattr(resolved_npc, 'provider'):
|
|
303
|
-
npc_provider = resolved_npc.provider
|
|
304
|
-
|
|
305
|
-
func_params = {
|
|
306
|
-
k: v for k, v in {
|
|
307
|
-
'prompt': query,
|
|
308
|
-
'text': query,
|
|
309
|
-
'npc': resolved_npc,
|
|
310
|
-
'team': resolved_team,
|
|
311
|
-
'context': params.get('context', ''),
|
|
312
|
-
'model': npc_model or 'gpt-4o-mini',
|
|
313
|
-
'provider': npc_provider or 'openai'
|
|
314
|
-
}.items() if k in sig.parameters
|
|
315
|
-
}
|
|
316
|
-
|
|
317
473
|
try:
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
474
|
+
if is_jinx:
|
|
475
|
+
# Execute as jinx
|
|
476
|
+
result_value = self._execute_jinx(
|
|
477
|
+
func_name,
|
|
478
|
+
query,
|
|
479
|
+
npc_ref,
|
|
480
|
+
params.get('context', '')
|
|
481
|
+
)
|
|
482
|
+
else:
|
|
483
|
+
# Execute as llm_func
|
|
484
|
+
sig = py_inspect.signature(func)
|
|
485
|
+
|
|
486
|
+
# Extract model/provider from NPC if available
|
|
487
|
+
npc_model = None
|
|
488
|
+
npc_provider = None
|
|
489
|
+
if resolved_npc and hasattr(resolved_npc, 'model'):
|
|
490
|
+
npc_model = resolved_npc.model
|
|
491
|
+
if resolved_npc and hasattr(resolved_npc, 'provider'):
|
|
492
|
+
npc_provider = resolved_npc.provider
|
|
493
|
+
|
|
494
|
+
func_params = {
|
|
495
|
+
k: v for k, v in {
|
|
496
|
+
'prompt': query,
|
|
497
|
+
'text': query,
|
|
498
|
+
'npc': resolved_npc,
|
|
499
|
+
'team': resolved_team,
|
|
500
|
+
'context': params.get('context', ''),
|
|
501
|
+
'model': npc_model or 'gpt-4o-mini',
|
|
502
|
+
'provider': npc_provider or 'openai'
|
|
503
|
+
}.items() if k in sig.parameters
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
result = func(**func_params)
|
|
507
|
+
result_value = (result.get("response", "")
|
|
508
|
+
if isinstance(result, dict)
|
|
509
|
+
else str(result))
|
|
510
|
+
|
|
511
|
+
print(f"OK ({len(str(result_value))} chars)")
|
|
323
512
|
except Exception as e:
|
|
324
513
|
print(f"ERROR: {e}")
|
|
325
514
|
result_value = None
|
|
@@ -333,20 +522,22 @@ class NPCSQLOperations:
|
|
|
333
522
|
# --- SQL Model Definition ---
|
|
334
523
|
class SQLModel:
|
|
335
524
|
def __init__(
|
|
336
|
-
self,
|
|
337
|
-
name: str,
|
|
338
|
-
content: str,
|
|
339
|
-
path: str,
|
|
340
|
-
npc_directory: str
|
|
525
|
+
self,
|
|
526
|
+
name: str,
|
|
527
|
+
content: str,
|
|
528
|
+
path: str,
|
|
529
|
+
npc_directory: str,
|
|
530
|
+
additional_functions: Optional[List[str]] = None
|
|
341
531
|
):
|
|
342
532
|
self.name = name
|
|
343
533
|
self.content = content
|
|
344
534
|
self.path = path
|
|
345
535
|
self.npc_directory = npc_directory
|
|
346
|
-
|
|
536
|
+
self.additional_functions = additional_functions or []
|
|
537
|
+
|
|
347
538
|
config_match = re.search(
|
|
348
|
-
r'\{\{[\s]*config\((.*?)\)[\s]*\}\}',
|
|
349
|
-
content,
|
|
539
|
+
r'\{\{[\s]*config\((.*?)\)[\s]*\}\}',
|
|
540
|
+
content,
|
|
350
541
|
re.DOTALL
|
|
351
542
|
)
|
|
352
543
|
if config_match:
|
|
@@ -356,7 +547,7 @@ class SQLModel:
|
|
|
356
547
|
|
|
357
548
|
self.dependencies = self._extract_dependencies()
|
|
358
549
|
self.has_ai_function = self._check_ai_functions()
|
|
359
|
-
|
|
550
|
+
|
|
360
551
|
# DEBUG print to confirm if AI functions are found
|
|
361
552
|
self.ai_functions = self._extract_ai_functions()
|
|
362
553
|
if self.ai_functions:
|
|
@@ -397,9 +588,15 @@ class SQLModel:
|
|
|
397
588
|
if name.startswith('_'):
|
|
398
589
|
continue
|
|
399
590
|
obj = getattr(llm_funcs, name)
|
|
400
|
-
if (isinstance(obj, types.FunctionType) or
|
|
591
|
+
if (isinstance(obj, types.FunctionType) or
|
|
401
592
|
(isinstance(obj, types.MethodType) and obj.__self__ is not None)):
|
|
402
593
|
available_functions.append(name.lower()) # Store as lowercase for comparison
|
|
594
|
+
|
|
595
|
+
# Add any additional functions (e.g., team jinxs)
|
|
596
|
+
for fn in self.additional_functions:
|
|
597
|
+
fn_lower = fn.lower()
|
|
598
|
+
if fn_lower not in available_functions:
|
|
599
|
+
available_functions.append(fn_lower)
|
|
403
600
|
|
|
404
601
|
for match in matches:
|
|
405
602
|
full_call_string = match.group(0).strip()
|
|
@@ -496,9 +693,17 @@ class ModelCompiler:
|
|
|
496
693
|
try:
|
|
497
694
|
self.npc_team = Team(team_path=npc_directory)
|
|
498
695
|
self.npc_operations.npc_loader = self.npc_team
|
|
696
|
+
# Load team jinxs as NQL functions
|
|
697
|
+
self.npc_operations.load_team_jinxs(self.npc_team)
|
|
499
698
|
except Exception as e:
|
|
500
699
|
self.npc_team = None
|
|
501
700
|
print(f"Warning: Could not load NPC team from {npc_directory}. AI functions relying on NPC context might fail: {e}")
|
|
701
|
+
|
|
702
|
+
# Initialize Jinja context for template processing
|
|
703
|
+
self.jinja_context = NQLJinjaContext(
|
|
704
|
+
team=self.npc_team,
|
|
705
|
+
npc_operations=self.npc_operations
|
|
706
|
+
)
|
|
502
707
|
|
|
503
708
|
def _get_engine(self, source_name: str) -> Engine:
|
|
504
709
|
if source_name.lower() == 'local' or not self.external_engines:
|
|
@@ -516,19 +721,27 @@ class ModelCompiler:
|
|
|
516
721
|
def discover_models(self):
|
|
517
722
|
self.models = {}
|
|
518
723
|
sql_files = list(self.models_dir.glob("**/*.sql"))
|
|
519
|
-
|
|
724
|
+
|
|
725
|
+
# Get list of available jinx names for NQL function recognition
|
|
726
|
+
additional_funcs = list(self.npc_operations.jinx_map.keys())
|
|
727
|
+
|
|
520
728
|
for sql_file in sql_files:
|
|
521
729
|
model_name = sql_file.stem
|
|
522
730
|
with open(sql_file, "r") as f:
|
|
523
731
|
content = f.read()
|
|
524
|
-
|
|
732
|
+
|
|
733
|
+
# Process Jinja templates ({% npc(...) %}, {% team.forenpc %}, etc.)
|
|
734
|
+
if JINJA_AVAILABLE and '{%' in content:
|
|
735
|
+
content = self.jinja_context.render_template(content)
|
|
736
|
+
|
|
525
737
|
self.models[model_name] = SQLModel(
|
|
526
|
-
model_name,
|
|
527
|
-
content,
|
|
528
|
-
str(sql_file),
|
|
529
|
-
str(sql_file.parent)
|
|
738
|
+
model_name,
|
|
739
|
+
content,
|
|
740
|
+
str(sql_file),
|
|
741
|
+
str(sql_file.parent),
|
|
742
|
+
additional_functions=additional_funcs
|
|
530
743
|
)
|
|
531
|
-
|
|
744
|
+
|
|
532
745
|
return self.models
|
|
533
746
|
|
|
534
747
|
def build_dag(self) -> Dict[str, Set[str]]:
|
npcpy/work/browser.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Global browser session storage for selenium automation.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
_sessions = {}
|
|
6
|
+
|
|
7
|
+
def get_sessions():
|
|
8
|
+
return _sessions
|
|
9
|
+
|
|
10
|
+
def get_current_driver():
|
|
11
|
+
current = _sessions.get('current')
|
|
12
|
+
if current and current in _sessions:
|
|
13
|
+
return _sessions[current]
|
|
14
|
+
return None
|
|
15
|
+
|
|
16
|
+
def set_driver(session_id, driver):
|
|
17
|
+
_sessions[session_id] = driver
|
|
18
|
+
_sessions['current'] = session_id
|
|
19
|
+
|
|
20
|
+
def close_current():
|
|
21
|
+
current = _sessions.get('current')
|
|
22
|
+
if current and current in _sessions:
|
|
23
|
+
try:
|
|
24
|
+
_sessions[current].quit()
|
|
25
|
+
except:
|
|
26
|
+
pass
|
|
27
|
+
del _sessions[current]
|
|
28
|
+
_sessions['current'] = None
|
|
29
|
+
return True
|
|
30
|
+
return False
|
|
@@ -1,21 +1,22 @@
|
|
|
1
1
|
npcpy/__init__.py,sha256=uJcJGjR1mWvE69GySNAufkgiRwJA28zdObDBWaxp0tY,505
|
|
2
|
-
npcpy/
|
|
2
|
+
npcpy/build_funcs.py,sha256=vOz6pjV0zS-kYKo0ux-pn9AcppVaR8KIDi2ldOxb3RQ,7479
|
|
3
|
+
npcpy/llm_funcs.py,sha256=98JRBeRNwnzxkcR6iypSQdm8OD7p4KcdKi8NDRipd4k,75492
|
|
3
4
|
npcpy/main.py,sha256=RWoRIj6VQLxKdOKvdVyaq2kwG35oRpeXPvp1CAAoG-w,81
|
|
4
5
|
npcpy/ml_funcs.py,sha256=UI7k7JR4XOH_VXR-xxLaO4r9Kyx_jBaEnp3TUIY7ZLQ,22657
|
|
5
6
|
npcpy/npc_array.py,sha256=fVTxcMiXV-lvltmuwaRnTU9D3ikPq3-7k5wzp7MA5OY,40224
|
|
6
|
-
npcpy/npc_compiler.py,sha256=
|
|
7
|
-
npcpy/npc_sysenv.py,sha256=
|
|
7
|
+
npcpy/npc_compiler.py,sha256=trMvZHZjGDHo0TvjxZc8QJZXit78-rPDpxU0wsxtocw,118139
|
|
8
|
+
npcpy/npc_sysenv.py,sha256=E4_C0UxuIasQ0Ie--CVty3d6OfV9rHP86OhEelVZhLs,36780
|
|
8
9
|
npcpy/npcs.py,sha256=eExuVsbTfrRobTRRptRpDm46jCLWUgbvy4_U7IUQo-c,744
|
|
9
|
-
npcpy/serve.py,sha256
|
|
10
|
+
npcpy/serve.py,sha256=-Aia3kmjozo1aSQzIcEWZP7IcRSHyp5S4qTboX0iYgM,192944
|
|
10
11
|
npcpy/tools.py,sha256=A5_oVmZkzGnI3BI-NmneuxeXQq-r29PbpAZP4nV4jrc,5303
|
|
11
12
|
npcpy/data/__init__.py,sha256=1tcoChR-Hjn905JDLqaW9ElRmcISCTJdE7BGXPlym2Q,642
|
|
12
13
|
npcpy/data/audio.py,sha256=3qryGXnWHa4JFMonjuX-lf0fCrF8jmbHe7mHAuOdua0,12397
|
|
13
14
|
npcpy/data/data_models.py,sha256=q7xpI4_nK5HvlOE1XB5u5nFQs4SE5zcgt0kIZJF2dhs,682
|
|
14
15
|
npcpy/data/image.py,sha256=UQcioNPDd5HYMLL_KStf45SuiIPXDcUY-dEFHwSWUeE,6564
|
|
15
|
-
npcpy/data/load.py,sha256=
|
|
16
|
+
npcpy/data/load.py,sha256=rVe1xSHerIpo6MDaY5eIeqRSm0gssX5sHukNsUNVwJw,9228
|
|
16
17
|
npcpy/data/text.py,sha256=jP0a1qZZaSJdK-LdZTn2Jjdxqmkd3efxDLEoxflJQeY,5010
|
|
17
18
|
npcpy/data/video.py,sha256=H-V3mTu_ktD9u-QhYeo4aW3u9z0AtoAdRZmvRPEpE98,2887
|
|
18
|
-
npcpy/data/web.py,sha256=
|
|
19
|
+
npcpy/data/web.py,sha256=pcjCLVAoqfw9enV5a7Dg1A_V7USG0302e6C7wUz2UgE,5235
|
|
19
20
|
npcpy/ft/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
21
|
npcpy/ft/diff.py,sha256=2-NbY0p0CP5Qr9mnnncxRBwzmxRq9NKcl8B5BeT1vQ4,12319
|
|
21
22
|
npcpy/ft/ge.py,sha256=0VzIiXq2wCzGcK1x0Wd-myJ3xRf-FNaPg0GkHEZegUM,3552
|
|
@@ -27,14 +28,15 @@ npcpy/ft/usft.py,sha256=O025GGYGZQf2ZVLowyAmBwh5bJyuy2dUAM6v03YcboY,3435
|
|
|
27
28
|
npcpy/gen/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
29
|
npcpy/gen/audio_gen.py,sha256=w4toESu7nmli1T5FOwRRCGC_QK9W-SMWknYYkbRv9jE,635
|
|
29
30
|
npcpy/gen/embeddings.py,sha256=QStTJ2ELiC379OEZsLEgGGIIFD267Y8zQchs7HRn2Zg,2089
|
|
30
|
-
npcpy/gen/image_gen.py,sha256=
|
|
31
|
+
npcpy/gen/image_gen.py,sha256=SOZYpvlxSiAdDK9j750OEBKjm22OUNdXg1kQ10sJSy0,21853
|
|
31
32
|
npcpy/gen/ocr.py,sha256=rgmXWHrCYX1Po-qG_LrNFbVYEZ8aaupxFTgparcoB_Y,6554
|
|
32
|
-
npcpy/gen/response.py,sha256=
|
|
33
|
+
npcpy/gen/response.py,sha256=3h-DgE1VbB9swbK7EAPpsofclEqXCpd4wSH63SmwhEQ,43401
|
|
33
34
|
npcpy/gen/video_gen.py,sha256=RFi3Zcq_Hn3HIcfoF3mijQ6G7RYFZaM_9pjPTh-8E64,3239
|
|
35
|
+
npcpy/gen/world_gen.py,sha256=_8ytE7E3QVQ5qiX8DmOby-xd0d9zV20rRI6Wkpf-qcY,18922
|
|
34
36
|
npcpy/memory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
35
|
-
npcpy/memory/command_history.py,sha256=
|
|
37
|
+
npcpy/memory/command_history.py,sha256=EEaBscup0YGA99ZHV8AzYBGUwRiI0nBTOPmLWvCbY-A,60362
|
|
36
38
|
npcpy/memory/kg_vis.py,sha256=TrQQCRh_E7Pyr-GPAHLSsayubAfGyf4HOEFrPB6W86Q,31280
|
|
37
|
-
npcpy/memory/knowledge_graph.py,sha256=
|
|
39
|
+
npcpy/memory/knowledge_graph.py,sha256=FsMptito-wUcYOpRWEUxyTcmXPr-lxgHlMSy9xN7jAA,48677
|
|
38
40
|
npcpy/memory/memory_processor.py,sha256=6PfVnSBA9ag5EhHJinXoODfEPTlDDoaT0PtCCuZO6HI,2598
|
|
39
41
|
npcpy/memory/search.py,sha256=glN6WYzaixcoDphTEHAXSMX3vKZGjR12Jx9YVL_gYfE,18433
|
|
40
42
|
npcpy/mix/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -44,14 +46,15 @@ npcpy/sql/ai_function_tools.py,sha256=ZCpjVHtaMRdL2dXxbQy5NhhjtPrVViGT1wyEl8ADrk
|
|
|
44
46
|
npcpy/sql/database_ai_adapters.py,sha256=CMlNGOhmJZhGB47RPvLIMqB61m_eYPVg1lwx42_b0jQ,6865
|
|
45
47
|
npcpy/sql/database_ai_functions.py,sha256=XQCmaFOE1lNCnwrLTNpotYOlv6sx41bb8hxZI_sqpy8,6335
|
|
46
48
|
npcpy/sql/model_runner.py,sha256=hJZ7hx2mwI-8DAh47Q6BwOsRjx30-HzebL4ajEUO4HA,5734
|
|
47
|
-
npcpy/sql/npcsql.py,sha256=
|
|
49
|
+
npcpy/sql/npcsql.py,sha256=5pA2csm1igRSAPGoFYeTHo3dQRIVTvvMH1QMyMhPrcI,44663
|
|
48
50
|
npcpy/sql/sql_model_compiler.py,sha256=G-0dpTlgzc-dXy9YEsdWGjO8xaQ3jFNbc6oUja1Ef4M,5364
|
|
49
51
|
npcpy/work/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
52
|
+
npcpy/work/browser.py,sha256=p2PeaoZdAXipFuAgKCCB3aXXLE_p3yIRqC87KlZKZWc,679
|
|
50
53
|
npcpy/work/desktop.py,sha256=F3I8mUtJp6LAkXodsh8hGZIncoads6c_2Utty-0EdDA,2986
|
|
51
54
|
npcpy/work/plan.py,sha256=QyUwg8vElWiHuoS-xK4jXTxxHvkMD3VkaCEsCmrEPQk,8300
|
|
52
55
|
npcpy/work/trigger.py,sha256=P1Y8u1wQRsS2WACims_2IdkBEar-iBQix-2TDWoW0OM,9948
|
|
53
|
-
npcpy-1.3.
|
|
54
|
-
npcpy-1.3.
|
|
55
|
-
npcpy-1.3.
|
|
56
|
-
npcpy-1.3.
|
|
57
|
-
npcpy-1.3.
|
|
56
|
+
npcpy-1.3.6.dist-info/licenses/LICENSE,sha256=j0YPvce7Ng9e32zYOu0EmXjXeJ0Nwawd0RA3uSGGH4E,1070
|
|
57
|
+
npcpy-1.3.6.dist-info/METADATA,sha256=CozApjX0lQ0YzAM2f2tHEpZAdd5gLRPcGkOgr70IG50,37884
|
|
58
|
+
npcpy-1.3.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
59
|
+
npcpy-1.3.6.dist-info/top_level.txt,sha256=g1pbSvrOOncB74Bg5-J0Olg4V0A5VzDw-Xz5YObq8BU,6
|
|
60
|
+
npcpy-1.3.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|