pasm-framework 0.5.2__tar.gz → 0.5.3__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.
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/PKG-INFO +1 -1
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/__init__.py +109 -1
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/plugins/builtins/web_gateway.py +109 -2
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework.egg-info/PKG-INFO +1 -1
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pyproject.toml +1 -1
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/LICENSE +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/README.md +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/__main__.py +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/adapter.py +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/application.py +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/config.py +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/demo.py +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/discovery.py +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/errors.py +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/plugins/__init__.py +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/plugins/builtins/__init__.py +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/plugins/builtins/_web_assets.py +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/plugins/builtins/cognitive_api.py +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/plugins/builtins/knowledge_base.py +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/plugins/builtins/llm_responder.py +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/plugins/builtins/observability.py +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/plugins/builtins/safety.py +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/plugins/builtins/sessions.py +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/plugins/builtins/warmth.py +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/plugins/core.py +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/plugins/registry.py +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/py.typed +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/scaffold.py +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/service.py +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/simple.py +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/skill.py +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework.egg-info/SOURCES.txt +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework.egg-info/dependency_links.txt +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework.egg-info/entry_points.txt +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework.egg-info/requires.txt +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework.egg-info/top_level.txt +0 -0
- {pasm_framework-0.5.2 → pasm_framework-0.5.3}/setup.cfg +0 -0
|
@@ -39,7 +39,7 @@ PASM V1→V2 升级时,**不重写 4 个产品智能体 + 3 个技能**。手
|
|
|
39
39
|
"""
|
|
40
40
|
from __future__ import annotations
|
|
41
41
|
|
|
42
|
-
__version__ = "0.5.
|
|
42
|
+
__version__ = "0.5.3"
|
|
43
43
|
|
|
44
44
|
from .adapter import ( # noqa: F401
|
|
45
45
|
DomainAdapter,
|
|
@@ -332,6 +332,114 @@ def selftest() -> bool:
|
|
|
332
332
|
check(_ProbeHandler(_g3, "/console")._authorized("/console"),
|
|
333
333
|
"网关:无令牌配置时保持开放(兼容本机开发)")
|
|
334
334
|
|
|
335
|
+
# ---- ★ v0.5.3 自定义路由(应用侧扩展接口)----
|
|
336
|
+
# 这段**必须真起服务器**:曾经的缺陷正是"register_route 写好了、
|
|
337
|
+
# 却没接进 HTTP 分派"——假 server 直接调 _guard 是测不出来的。
|
|
338
|
+
# 症状极具迷惑性:服务报 healthy、管理台照样能聊,
|
|
339
|
+
# **只有应用自己注册的接口全 404,零提示**。
|
|
340
|
+
import json as _json
|
|
341
|
+
import socket as _socket
|
|
342
|
+
import urllib.error as _urlerr
|
|
343
|
+
import urllib.request as _urlreq
|
|
344
|
+
|
|
345
|
+
def _free_port() -> int:
|
|
346
|
+
_s = _socket.socket()
|
|
347
|
+
_s.bind(("127.0.0.1", 0))
|
|
348
|
+
_p = _s.getsockname()[1]
|
|
349
|
+
_s.close()
|
|
350
|
+
return _p
|
|
351
|
+
|
|
352
|
+
_port = _free_port()
|
|
353
|
+
_rt = type(gw)({"token": "ADMIN-RT", "public_token": "PUB-RT"})
|
|
354
|
+
|
|
355
|
+
def _ping(q, b):
|
|
356
|
+
return 200, {"pong": q.get("x", "")}
|
|
357
|
+
|
|
358
|
+
def _echo(q, b):
|
|
359
|
+
return 200, {"got": b}
|
|
360
|
+
|
|
361
|
+
def _bad(q, b):
|
|
362
|
+
raise ValueError("x 必须是数字")
|
|
363
|
+
|
|
364
|
+
def _crash(q, b):
|
|
365
|
+
raise RuntimeError("boom")
|
|
366
|
+
|
|
367
|
+
_rt.register_route("GET", "/api/app/ping", _ping)
|
|
368
|
+
_rt.register_route("post", "/api/app/echo", _echo) # 小写应被归一
|
|
369
|
+
_rt.register_route("GET", "/api/app/bad", _bad)
|
|
370
|
+
_rt.register_route("GET", "/api/app/crash", _crash)
|
|
371
|
+
|
|
372
|
+
def _call(method, path, tok=None, body=None):
|
|
373
|
+
req = _urlreq.Request("http://127.0.0.1:%d%s" % (_port, path),
|
|
374
|
+
method=method)
|
|
375
|
+
if tok:
|
|
376
|
+
req.add_header("Authorization", "Bearer " + tok)
|
|
377
|
+
data = None
|
|
378
|
+
if body is not None:
|
|
379
|
+
data = _json.dumps(body).encode("utf-8")
|
|
380
|
+
req.add_header("Content-Type", "application/json")
|
|
381
|
+
try:
|
|
382
|
+
with _urlreq.urlopen(req, data=data, timeout=5) as r:
|
|
383
|
+
return r.status, _json.loads(r.read().decode("utf-8") or "{}")
|
|
384
|
+
except _urlerr.HTTPError as ex:
|
|
385
|
+
return ex.code, _json.loads(ex.read().decode("utf-8") or "{}")
|
|
386
|
+
|
|
387
|
+
try:
|
|
388
|
+
_rt.start(host="127.0.0.1", port=_port)
|
|
389
|
+
# ★ 下面这几条正是过去会静默失效的地方:注册了 → 真能打到。
|
|
390
|
+
_st, _pl = _call("GET", "/api/app/ping?x=42", "ADMIN-RT")
|
|
391
|
+
check(_st == 200 and _pl.get("pong") == "42",
|
|
392
|
+
"★ 自定义路由真能命中且 query 透传(%s %s)" % (_st, _pl))
|
|
393
|
+
_st, _pl = _call("POST", "/api/app/echo", "ADMIN-RT", {"a": 1})
|
|
394
|
+
check(_st == 200 and _pl.get("got") == {"a": 1},
|
|
395
|
+
"★ 自定义路由 POST 收到 JSON body(%s %s)" % (_st, _pl))
|
|
396
|
+
_st, _ = _call("GET", "/api/app/nope", "ADMIN-RT")
|
|
397
|
+
check(_st == 404, "自定义路由:未注册路径 → 404(不误吞)")
|
|
398
|
+
# ★ 鉴权不变量:自定义路由**一律管理作用域**。
|
|
399
|
+
_st, _ = _call("GET", "/api/app/ping?x=1")
|
|
400
|
+
check(_st == 401, "★ 自定义路由无令牌 → 401")
|
|
401
|
+
_st, _ = _call("GET", "/api/app/ping?x=1", "PUB-RT")
|
|
402
|
+
check(_st == 401,
|
|
403
|
+
"★ 自定义路由拒绝公开令牌(不因\"自定义\"就降级作用域)")
|
|
404
|
+
_st, _pl = _call("GET", "/api/app/bad", "ADMIN-RT")
|
|
405
|
+
check(_st == 400 and "x 必须是数字" in str(_pl.get("error")),
|
|
406
|
+
"自定义路由:handler 抛 ValueError → 400(调用方可纠错)")
|
|
407
|
+
_st, _pl = _call("GET", "/api/app/crash", "ADMIN-RT")
|
|
408
|
+
check(_st == 500 and "boom" in str(_pl.get("error")),
|
|
409
|
+
"自定义路由:handler 抛其它异常 → 500(不把服务打挂)")
|
|
410
|
+
_st, _pl = _call("GET", "/healthz")
|
|
411
|
+
check(_st == 200 and _pl.get("custom_routes") == 4,
|
|
412
|
+
"★ /healthz 暴露 custom_routes 条数(=0 就是\"没注册上\"信号)")
|
|
413
|
+
finally:
|
|
414
|
+
_rt.stop()
|
|
415
|
+
|
|
416
|
+
# 注册语义(不需要起服务器)
|
|
417
|
+
check(_rt.match_route("POST", "/api/app/echo") is not None,
|
|
418
|
+
"match_route 对 method 大小写不敏感(注册 post → 匹配 POST)")
|
|
419
|
+
check(_rt.match_route("GET", "/api/app/none") is None,
|
|
420
|
+
"match_route 未注册 → None")
|
|
421
|
+
_hit = _rt.match_route("GET", "/api/app/ping")
|
|
422
|
+
check(_hit is not None and _hit[1] is True,
|
|
423
|
+
"★ 自定义路由一律管理作用域(need_admin 恒为 True)")
|
|
424
|
+
# ★ 注册到内置路径会被内置分支遮蔽 → 必须**拒绝**而不是静默接受。
|
|
425
|
+
for _rp in ("/healthz", "/api/chat", "/console",
|
|
426
|
+
CognitiveAPI.PREFIX.rstrip("/")):
|
|
427
|
+
try:
|
|
428
|
+
_rt.register_route("GET", _rp, _ping)
|
|
429
|
+
check(False, "注册内置路径 %s 应被拒绝(否则静默失效)" % _rp)
|
|
430
|
+
except ValueError:
|
|
431
|
+
check(True, "注册内置路径 %s 被拒绝(不静默失效)" % _rp)
|
|
432
|
+
try:
|
|
433
|
+
_rt.register_route("GET", "/api/app/x", "not-callable")
|
|
434
|
+
check(False, "handler 不可调用应抛 TypeError")
|
|
435
|
+
except TypeError:
|
|
436
|
+
check(True, "自定义路由:handler 不可调用抛 TypeError")
|
|
437
|
+
try:
|
|
438
|
+
_rt.register_route("GET", "no-leading-slash", _ping)
|
|
439
|
+
check(False, "path 缺前导 / 应被拒绝")
|
|
440
|
+
except ValueError:
|
|
441
|
+
check(True, "自定义路由:path 缺前导 / 被拒绝")
|
|
442
|
+
|
|
335
443
|
# ---- 认知 HTTP API(/api/cog/*)
|
|
336
444
|
check(getattr(gw, "cognitive", None) is not None
|
|
337
445
|
and gw.cognitive.available,
|
{pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/plugins/builtins/web_gateway.py
RENAMED
|
@@ -72,7 +72,7 @@ import threading
|
|
|
72
72
|
import time
|
|
73
73
|
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
|
74
74
|
from typing import Any, Dict, List, Optional
|
|
75
|
-
from urllib.parse import unquote
|
|
75
|
+
from urllib.parse import parse_qs, unquote
|
|
76
76
|
|
|
77
77
|
from ..core import BasePlugin, PluginContext
|
|
78
78
|
from ._web_assets import CONSOLE_HTML, EMBED_JS
|
|
@@ -98,6 +98,18 @@ _WIDGET_PATHS = ("/", "/widget", "/index.html")
|
|
|
98
98
|
#: 太小 → 一句话被切断、语义破碎。800 字符约合中文 2–4 段,是"能独立成条"的量级。
|
|
99
99
|
_CHUNK_CHARS = 800
|
|
100
100
|
|
|
101
|
+
#: 内置路由占用的路径。**自定义路由注册到这些路径上会被内置分支遮蔽**
|
|
102
|
+
#: (``do_GET``/``do_POST`` 里内置分支在前、``_custom`` 在最后),
|
|
103
|
+
#: 表现为"注册成功但请求仍走内置逻辑 / 仍 404",且**零提示**。
|
|
104
|
+
#: 所以 :meth:`WebGatewayPlugin.register_route` 直接拒绝 —— 宁可启动即报错,
|
|
105
|
+
#: 也不要"注册了却零生效"。``/api/cog/*`` 前缀同理,归认知接口独占。
|
|
106
|
+
_RESERVED_PATHS = frozenset((
|
|
107
|
+
"/", "/widget", "/index.html", "/embed.js", "/console", "/healthz",
|
|
108
|
+
"/api/chat", "/api/chat/stream", "/api/ingest", "/api/ingest/text",
|
|
109
|
+
"/api/plugins", "/api/summary", "/api/kb/stats", "/api/sessions",
|
|
110
|
+
"/api/sessions/reset",
|
|
111
|
+
))
|
|
112
|
+
|
|
101
113
|
#: 短于此长度的段落视为"碎片"(小标题、单句),会被并入相邻段而不是独立成条 ——
|
|
102
114
|
#: 一个只有标题的块既是噪声、又几乎匹配不到任何问句。
|
|
103
115
|
_MIN_CHUNK = 24
|
|
@@ -455,6 +467,8 @@ class _Handler(BaseHTTPRequestHandler):
|
|
|
455
467
|
self._send(200, gw.sessions_info())
|
|
456
468
|
elif _COG_PREFIX.rstrip("/") in path or path.startswith(_COG_PREFIX):
|
|
457
469
|
self._cog("GET")
|
|
470
|
+
elif self._custom("GET"):
|
|
471
|
+
pass
|
|
458
472
|
else:
|
|
459
473
|
self._send(404, {"error": "not found", "paths": [
|
|
460
474
|
"/", "/widget", "/embed.js", "/console", "/healthz",
|
|
@@ -551,6 +565,8 @@ class _Handler(BaseHTTPRequestHandler):
|
|
|
551
565
|
self._send(200, {"ok": gw.reset_session(sid), "session_id": sid})
|
|
552
566
|
elif _COG_PREFIX.rstrip("/") in path or path.startswith(_COG_PREFIX):
|
|
553
567
|
self._cog("POST")
|
|
568
|
+
elif self._custom("POST"):
|
|
569
|
+
pass
|
|
554
570
|
else:
|
|
555
571
|
self._send(404, {"error": "not found"})
|
|
556
572
|
|
|
@@ -581,12 +597,50 @@ class _Handler(BaseHTTPRequestHandler):
|
|
|
581
597
|
return
|
|
582
598
|
self._send(out[0], out[1])
|
|
583
599
|
|
|
600
|
+
# ---- 自定义路由分派 --------------------------------------------
|
|
601
|
+
|
|
602
|
+
def _custom(self, method: str) -> bool:
|
|
603
|
+
"""尝试用应用注册的自定义路由处理;返回 True 表示已处理(含已回错误)。
|
|
604
|
+
|
|
605
|
+
★ 鉴权在这里做,而且 **admin 恒为 True**:自定义路由能写数据,
|
|
606
|
+
绝不能因为"是应用自己注册的"就降级成公开作用域。
|
|
607
|
+
"""
|
|
608
|
+
path = self.path.split("?")[0]
|
|
609
|
+
hit = self._gw.match_route(method, path)
|
|
610
|
+
if hit is None:
|
|
611
|
+
return False
|
|
612
|
+
handler, need_admin = hit
|
|
613
|
+
# ★ 这里是**第二道**守卫(第一道在 do_GET/do_POST 的入口处)。刻意冗余:
|
|
614
|
+
# 入口那道是所有路径共用的,一旦将来有人在它之前插入新分支,这道就是兜底。
|
|
615
|
+
# 反例对照实测过:单独破坏任一道,selftest 仍全绿(另一道拦住了);
|
|
616
|
+
# 必须**同时**破坏两道才会变红。所以 selftest 断言的是**结果**
|
|
617
|
+
# (公开令牌打自定义路由必须 401),而不是"某一行代码生效"。
|
|
618
|
+
# —— 别把它当死代码删掉。
|
|
619
|
+
if not self._guard(path, admin=need_admin):
|
|
620
|
+
return True
|
|
621
|
+
_, _, query = self.path.partition("?")
|
|
622
|
+
body: Dict[str, Any] = {}
|
|
623
|
+
if method == "POST":
|
|
624
|
+
got = self._read_json()
|
|
625
|
+
if got is None:
|
|
626
|
+
return True
|
|
627
|
+
body = got
|
|
628
|
+
q = {k: v[-1] for k, v in parse_qs(query or "").items() if v}
|
|
629
|
+
try:
|
|
630
|
+
status, payload = handler(q, body)
|
|
631
|
+
except ValueError as ex: # 参数错 → 400(调用方可自行纠错)
|
|
632
|
+
status, payload = 400, {"error": str(ex)}
|
|
633
|
+
except Exception as ex: # noqa: BLE001
|
|
634
|
+
status, payload = 500, {"error": "%s: %s" % (type(ex).__name__, ex)}
|
|
635
|
+
self._send(status, payload)
|
|
636
|
+
return True
|
|
637
|
+
|
|
584
638
|
|
|
585
639
|
class WebGatewayPlugin(BasePlugin):
|
|
586
640
|
"""零依赖 HTTP 网关。"""
|
|
587
641
|
|
|
588
642
|
name = "web_gateway"
|
|
589
|
-
version = "0.3.
|
|
643
|
+
version = "0.3.1"
|
|
590
644
|
|
|
591
645
|
def __init__(self, config: Optional[Dict[str, Any]] = None) -> None:
|
|
592
646
|
super().__init__(config)
|
|
@@ -610,6 +664,56 @@ class WebGatewayPlugin(BasePlugin):
|
|
|
610
664
|
self._app: Optional[Any] = None
|
|
611
665
|
self._server: Optional[ThreadingHTTPServer] = None
|
|
612
666
|
self._thread: Optional[threading.Thread] = None
|
|
667
|
+
#: 自定义路由:{(METHOD, path): handler(query: dict, body: dict) -> (status, payload)}
|
|
668
|
+
self._routes: Dict[tuple, Any] = {}
|
|
669
|
+
|
|
670
|
+
# ---- 自定义路由 ----
|
|
671
|
+
|
|
672
|
+
def register_route(self, method: str, path: str, handler: Any) -> None:
|
|
673
|
+
"""注册一条自定义路由(应用侧扩展接口用)。
|
|
674
|
+
|
|
675
|
+
``handler(query: dict, body: dict) -> (status:int, payload:dict)``
|
|
676
|
+
|
|
677
|
+
为什么用运行时注册而不是配置项:handler 是**可调用对象**,
|
|
678
|
+
塞进 JSON 配置既不好序列化也不好测试。运行时注册最直接。
|
|
679
|
+
|
|
680
|
+
★ 这些路由与内置路由**共用同一套鉴权与限流** —— 别指望靠"自定义"绕开
|
|
681
|
+
管理作用域;那正是最容易出越权的地方。
|
|
682
|
+
"""
|
|
683
|
+
if not callable(handler):
|
|
684
|
+
raise TypeError("handler 必须可调用")
|
|
685
|
+
p = str(path or "").strip()
|
|
686
|
+
if not p.startswith("/"):
|
|
687
|
+
raise ValueError("自定义路由 path 必须以 / 开头:%r" % (path,))
|
|
688
|
+
if p in _RESERVED_PATHS or p == _COG_PREFIX.rstrip("/") \
|
|
689
|
+
or p.startswith(_COG_PREFIX):
|
|
690
|
+
raise ValueError(
|
|
691
|
+
"自定义路由不能注册到内置路径 %r —— 内置分支在前会把它遮蔽掉,"
|
|
692
|
+
"表现为\"注册成功但请求仍走内置逻辑 / 仍 404\"且零提示。"
|
|
693
|
+
"请换前缀,例如 /api/app/<你的接口>" % p)
|
|
694
|
+
self._routes[(method.upper(), p)] = handler
|
|
695
|
+
|
|
696
|
+
def match_route(self, method: str, path: str):
|
|
697
|
+
"""找一条自定义路由,返回 ``(handler, need_admin)`` 或 ``None``。
|
|
698
|
+
|
|
699
|
+
自定义路由**一律管理作用域**(``need_admin=True``)——认知与医疗类接口
|
|
700
|
+
都能写数据,不能像 ``/api/chat`` 那样对访客开放。
|
|
701
|
+
"""
|
|
702
|
+
h = self._routes.get((method.upper(), path))
|
|
703
|
+
return (h, True) if h else None
|
|
704
|
+
|
|
705
|
+
def routes_info(self) -> Dict[str, Any]:
|
|
706
|
+
"""已注册的自定义路由概览(**只暴露方法与路径,不暴露 handler**)。
|
|
707
|
+
|
|
708
|
+
存在的意义:``register_route`` 曾经因为"没接进 HTTP 分派"而**静默失效** ——
|
|
709
|
+
服务照样报 healthy、管理台照样能聊,只有应用自己的接口全 404,零提示。
|
|
710
|
+
现在把条数挂到 ``/healthz`` 上,部署后一眼能看出
|
|
711
|
+
"应用该注册的路由到底有没有注册上"(``custom_routes: 0`` 就是信号)。
|
|
712
|
+
"""
|
|
713
|
+
return {
|
|
714
|
+
"count": len(self._routes),
|
|
715
|
+
"routes": sorted("%s %s" % (m, p) for (m, p) in self._routes),
|
|
716
|
+
}
|
|
613
717
|
|
|
614
718
|
@property
|
|
615
719
|
def serve_token(self) -> str:
|
|
@@ -753,6 +857,9 @@ class WebGatewayPlugin(BasePlugin):
|
|
|
753
857
|
"host": self._host,
|
|
754
858
|
"port": self._port,
|
|
755
859
|
"auth_required": bool(self.token),
|
|
860
|
+
# 应用侧注册的自定义路由条数。**该是 0 还是非 0,部署方一眼可辨** ——
|
|
861
|
+
# 见 routes_info() 的注释:这类"注册了却没生效"过去是完全静默的。
|
|
862
|
+
"custom_routes": len(self._routes),
|
|
756
863
|
}
|
|
757
864
|
pm = getattr(self._app, "plugins", None) if self._app is not None else None
|
|
758
865
|
obs = pm.get("observability") if pm is not None else None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/plugins/builtins/_web_assets.py
RENAMED
|
File without changes
|
{pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/plugins/builtins/cognitive_api.py
RENAMED
|
File without changes
|
{pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/plugins/builtins/knowledge_base.py
RENAMED
|
File without changes
|
{pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/plugins/builtins/llm_responder.py
RENAMED
|
File without changes
|
{pasm_framework-0.5.2 → pasm_framework-0.5.3}/pasm_framework/plugins/builtins/observability.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|