amfs-http-server 0.1.0__tar.gz → 0.1.2__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.
- {amfs_http_server-0.1.0 → amfs_http_server-0.1.2}/PKG-INFO +1 -1
- {amfs_http_server-0.1.0 → amfs_http_server-0.1.2}/pyproject.toml +1 -1
- {amfs_http_server-0.1.0 → amfs_http_server-0.1.2}/src/amfs_http/models.py +3 -0
- {amfs_http_server-0.1.0 → amfs_http_server-0.1.2}/src/amfs_http/server.py +306 -18
- {amfs_http_server-0.1.0 → amfs_http_server-0.1.2}/src/amfs_http/tenant_middleware.py +7 -2
- {amfs_http_server-0.1.0 → amfs_http_server-0.1.2}/.gitignore +0 -0
- {amfs_http_server-0.1.0 → amfs_http_server-0.1.2}/src/amfs_http/__init__.py +0 -0
- {amfs_http_server-0.1.0 → amfs_http_server-0.1.2}/src/amfs_http/auth.py +0 -0
- {amfs_http_server-0.1.0 → amfs_http_server-0.1.2}/src/amfs_http/pro_proxy.py +0 -0
- {amfs_http_server-0.1.0 → amfs_http_server-0.1.2}/src/amfs_http/sse.py +0 -0
|
@@ -16,6 +16,7 @@ class WriteRequest(BaseModel):
|
|
|
16
16
|
memory_type: str = "fact"
|
|
17
17
|
shared: bool = True
|
|
18
18
|
branch: str = "main"
|
|
19
|
+
agent_id: str | None = None
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
class OutcomeRequest(BaseModel):
|
|
@@ -23,6 +24,7 @@ class OutcomeRequest(BaseModel):
|
|
|
23
24
|
outcome_type: str
|
|
24
25
|
causal_entry_keys: list[str] | None = None
|
|
25
26
|
causal_confidence: float = 1.0
|
|
27
|
+
agent_id: str | None = None
|
|
26
28
|
|
|
27
29
|
|
|
28
30
|
class SearchRequest(BaseModel):
|
|
@@ -42,6 +44,7 @@ class ContextRequest(BaseModel):
|
|
|
42
44
|
label: str
|
|
43
45
|
summary: str
|
|
44
46
|
source: str | None = None
|
|
47
|
+
agent_id: str | None = None
|
|
45
48
|
|
|
46
49
|
|
|
47
50
|
class CreateAPIKeyRequest(BaseModel):
|
|
@@ -79,12 +79,18 @@ async def _dashboard_tenant_middleware(request: Request, call_next):
|
|
|
79
79
|
_memory: AgentMemory | None = None
|
|
80
80
|
_sse_manager = SSEManager()
|
|
81
81
|
|
|
82
|
+
_immutable_trace_store = None
|
|
82
83
|
try:
|
|
83
84
|
from amfs_traces.api import mount_pro_routes
|
|
84
85
|
mount_pro_routes(app)
|
|
85
86
|
logger.info("Pro trace endpoints mounted at /api/v1/pro/traces")
|
|
87
|
+
|
|
88
|
+
from amfs_traces.store import PostgresImmutableTraceStore
|
|
89
|
+
from amfs_traces.crypto import seal, get_signing_key, get_signing_key_id
|
|
90
|
+
from amfs_traces.models import ImmutableDecisionTrace, TraceEntry, TraceExternalContext
|
|
91
|
+
_HAS_PRO_TRACES = True
|
|
86
92
|
except ImportError:
|
|
87
|
-
|
|
93
|
+
_HAS_PRO_TRACES = False
|
|
88
94
|
|
|
89
95
|
try:
|
|
90
96
|
from amfs_cortex_pro import mount_cortex_pro
|
|
@@ -218,6 +224,40 @@ async def health() -> dict[str, str]:
|
|
|
218
224
|
return {"status": "ok"}
|
|
219
225
|
|
|
220
226
|
|
|
227
|
+
@app.get("/api/v1/auth/whoami")
|
|
228
|
+
async def whoami(
|
|
229
|
+
request: Request,
|
|
230
|
+
_auth: str | None = Depends(verify_api_key),
|
|
231
|
+
) -> dict[str, Any]:
|
|
232
|
+
"""Return information about the authenticated caller.
|
|
233
|
+
|
|
234
|
+
When Pro middleware is active, this returns account, key type, scopes,
|
|
235
|
+
and rate limit info. In OSS mode, returns basic auth status.
|
|
236
|
+
"""
|
|
237
|
+
ctx = getattr(request.state, "tenant_ctx", None)
|
|
238
|
+
if ctx is not None:
|
|
239
|
+
return {
|
|
240
|
+
"authenticated": True,
|
|
241
|
+
"mode": "pro",
|
|
242
|
+
"account_id": str(ctx.account_id),
|
|
243
|
+
"actor_id": str(ctx.actor_id),
|
|
244
|
+
"key_type": ctx.key_type.value if ctx.key_type else None,
|
|
245
|
+
"scopes": [
|
|
246
|
+
{
|
|
247
|
+
"entity_path_pattern": s.entity_path_pattern,
|
|
248
|
+
"permission": s.permission.value,
|
|
249
|
+
}
|
|
250
|
+
for s in ctx.scopes
|
|
251
|
+
],
|
|
252
|
+
"rate_limit_rpm": ctx.rate_limit_rpm,
|
|
253
|
+
"is_admin": ctx.is_admin,
|
|
254
|
+
}
|
|
255
|
+
return {
|
|
256
|
+
"authenticated": _auth is not None,
|
|
257
|
+
"mode": "oss",
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
|
|
221
261
|
# ──────────────────────────────────────────────────────────────────────
|
|
222
262
|
# Entries
|
|
223
263
|
# ──────────────────────────────────────────────────────────────────────
|
|
@@ -252,16 +292,27 @@ async def write_entry(
|
|
|
252
292
|
}
|
|
253
293
|
mt = type_map.get(req.memory_type.lower(), MemoryType.FACT)
|
|
254
294
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
req.
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
295
|
+
original_agent = mem._tagger.agent_id if req.agent_id else None
|
|
296
|
+
if req.agent_id:
|
|
297
|
+
mem._tagger.agent_id = req.agent_id
|
|
298
|
+
try:
|
|
299
|
+
mem._adapter.ensure_agent(req.agent_id, mem.namespace)
|
|
300
|
+
except Exception:
|
|
301
|
+
pass
|
|
302
|
+
try:
|
|
303
|
+
entry = mem.write(
|
|
304
|
+
req.entity_path,
|
|
305
|
+
req.key,
|
|
306
|
+
req.value,
|
|
307
|
+
confidence=req.confidence,
|
|
308
|
+
pattern_refs=req.pattern_refs or None,
|
|
309
|
+
memory_type=mt,
|
|
310
|
+
shared=req.shared,
|
|
311
|
+
branch=req.branch,
|
|
312
|
+
)
|
|
313
|
+
finally:
|
|
314
|
+
if original_agent is not None:
|
|
315
|
+
mem._tagger.agent_id = original_agent
|
|
265
316
|
_sse_manager.broadcast(entry)
|
|
266
317
|
_audit_log(
|
|
267
318
|
"memory.write",
|
|
@@ -380,6 +431,98 @@ _OUTCOME_TYPE_MAP = {
|
|
|
380
431
|
}
|
|
381
432
|
|
|
382
433
|
|
|
434
|
+
def _get_immutable_store():
|
|
435
|
+
"""Lazily create the immutable trace store (Pro only)."""
|
|
436
|
+
global _immutable_trace_store
|
|
437
|
+
if _immutable_trace_store is not None:
|
|
438
|
+
return _immutable_trace_store
|
|
439
|
+
if not _HAS_PRO_TRACES:
|
|
440
|
+
return None
|
|
441
|
+
dsn = os.environ.get("AMFS_POSTGRES_DSN")
|
|
442
|
+
if not dsn:
|
|
443
|
+
return None
|
|
444
|
+
try:
|
|
445
|
+
import psycopg
|
|
446
|
+
from psycopg.rows import dict_row
|
|
447
|
+
conn = psycopg.connect(dsn, row_factory=dict_row, autocommit=True)
|
|
448
|
+
_immutable_trace_store = PostgresImmutableTraceStore(conn)
|
|
449
|
+
logger.info("Immutable trace store initialized")
|
|
450
|
+
return _immutable_trace_store
|
|
451
|
+
except Exception:
|
|
452
|
+
logger.debug("Failed to init immutable trace store", exc_info=True)
|
|
453
|
+
return None
|
|
454
|
+
|
|
455
|
+
|
|
456
|
+
_seal_sequence: dict[str, int] = {}
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
def _auto_seal_trace(mem: AgentMemory) -> str | None:
|
|
460
|
+
"""If Pro traces are available, auto-seal the last OSS trace as immutable."""
|
|
461
|
+
if not _HAS_PRO_TRACES:
|
|
462
|
+
return None
|
|
463
|
+
oss_trace = getattr(mem, "_last_trace", None)
|
|
464
|
+
if oss_trace is None:
|
|
465
|
+
return None
|
|
466
|
+
store = _get_immutable_store()
|
|
467
|
+
if store is None:
|
|
468
|
+
return None
|
|
469
|
+
try:
|
|
470
|
+
now = datetime.now(timezone.utc)
|
|
471
|
+
causal = [
|
|
472
|
+
TraceEntry(
|
|
473
|
+
entity_path=e.entity_path,
|
|
474
|
+
key=e.key,
|
|
475
|
+
version=e.version,
|
|
476
|
+
confidence=e.confidence,
|
|
477
|
+
value=e.value,
|
|
478
|
+
memory_type=getattr(e, "memory_type", None) or "fact",
|
|
479
|
+
written_by=getattr(e, "written_by", None),
|
|
480
|
+
read_at=getattr(e, "read_at", None) or now,
|
|
481
|
+
)
|
|
482
|
+
for e in (oss_trace.causal_entries or [])
|
|
483
|
+
]
|
|
484
|
+
contexts = [
|
|
485
|
+
TraceExternalContext(
|
|
486
|
+
label=c.label,
|
|
487
|
+
summary=c.summary,
|
|
488
|
+
source=getattr(c, "source", None),
|
|
489
|
+
recorded_at=getattr(c, "recorded_at", None) or now,
|
|
490
|
+
)
|
|
491
|
+
for c in (oss_trace.external_contexts or [])
|
|
492
|
+
]
|
|
493
|
+
|
|
494
|
+
session_id = mem.session_id
|
|
495
|
+
seq = _seal_sequence.get(session_id, 0)
|
|
496
|
+
parent_hash = store.get_latest_hash(session_id)
|
|
497
|
+
|
|
498
|
+
imm = ImmutableDecisionTrace(
|
|
499
|
+
agent_id=mem.agent_id,
|
|
500
|
+
session_id=session_id,
|
|
501
|
+
sequence_number=seq,
|
|
502
|
+
outcome_ref=oss_trace.outcome_ref,
|
|
503
|
+
outcome_type=oss_trace.outcome_type,
|
|
504
|
+
decision_summary=getattr(oss_trace, "decision_summary", None),
|
|
505
|
+
causal_entries=causal,
|
|
506
|
+
external_contexts=contexts,
|
|
507
|
+
created_at=datetime.now(timezone.utc),
|
|
508
|
+
)
|
|
509
|
+
sealed = seal(
|
|
510
|
+
imm,
|
|
511
|
+
get_signing_key(),
|
|
512
|
+
parent_hash=parent_hash,
|
|
513
|
+
sequence_number=seq,
|
|
514
|
+
signing_key_id=get_signing_key_id(),
|
|
515
|
+
)
|
|
516
|
+
saved = store.save(sealed)
|
|
517
|
+
_seal_sequence[session_id] = seq + 1
|
|
518
|
+
logger.info("Auto-sealed immutable trace %s for outcome %s",
|
|
519
|
+
saved.id, oss_trace.outcome_ref)
|
|
520
|
+
return str(saved.id)
|
|
521
|
+
except Exception:
|
|
522
|
+
logger.warning("Failed to auto-seal immutable trace", exc_info=True)
|
|
523
|
+
return None
|
|
524
|
+
|
|
525
|
+
|
|
383
526
|
@app.post("/api/v1/outcomes")
|
|
384
527
|
async def commit_outcome(
|
|
385
528
|
req: OutcomeRequest,
|
|
@@ -393,23 +536,40 @@ async def commit_outcome(
|
|
|
393
536
|
valid = ", ".join(_OUTCOME_TYPE_MAP.keys())
|
|
394
537
|
return {"error": f"Invalid outcome_type '{req.outcome_type}'. Must be one of: {valid}"}
|
|
395
538
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
539
|
+
original_agent = mem._tagger.agent_id if req.agent_id else None
|
|
540
|
+
if req.agent_id:
|
|
541
|
+
mem._tagger.agent_id = req.agent_id
|
|
542
|
+
try:
|
|
543
|
+
mem._adapter.ensure_agent(req.agent_id, mem.namespace)
|
|
544
|
+
except Exception:
|
|
545
|
+
pass
|
|
546
|
+
try:
|
|
547
|
+
entries = mem.commit_outcome(
|
|
548
|
+
req.outcome_ref,
|
|
549
|
+
otype,
|
|
550
|
+
causal_entry_keys=req.causal_entry_keys,
|
|
551
|
+
causal_confidence=req.causal_confidence,
|
|
552
|
+
)
|
|
553
|
+
finally:
|
|
554
|
+
if original_agent is not None:
|
|
555
|
+
mem._tagger.agent_id = original_agent
|
|
402
556
|
_audit_log(
|
|
403
557
|
"outcome.commit",
|
|
404
558
|
resource=req.outcome_ref,
|
|
405
559
|
ip_address=request.client.host if request.client else None,
|
|
406
560
|
)
|
|
407
|
-
|
|
561
|
+
|
|
562
|
+
immutable_trace_id = _auto_seal_trace(mem)
|
|
563
|
+
|
|
564
|
+
result: dict[str, Any] = {
|
|
408
565
|
"outcome_ref": req.outcome_ref,
|
|
409
566
|
"outcome_type": req.outcome_type,
|
|
410
567
|
"affected_entries": len(entries),
|
|
411
568
|
"entries": [_entry_to_response(e) for e in entries],
|
|
412
569
|
}
|
|
570
|
+
if immutable_trace_id:
|
|
571
|
+
result["immutable_trace_id"] = immutable_trace_id
|
|
572
|
+
return result
|
|
413
573
|
|
|
414
574
|
|
|
415
575
|
@app.get("/api/v1/outcomes")
|
|
@@ -1680,6 +1840,42 @@ async def resolve_pattern(
|
|
|
1680
1840
|
# ──────────────────────────────────────────────────────────────────────
|
|
1681
1841
|
|
|
1682
1842
|
|
|
1843
|
+
@app.get("/api/v1/pro/graph/neighbors")
|
|
1844
|
+
async def graph_neighbors(
|
|
1845
|
+
entity: str = Query(...),
|
|
1846
|
+
relation: str | None = Query(None),
|
|
1847
|
+
direction: str = Query("both"),
|
|
1848
|
+
min_confidence: float = Query(0.0),
|
|
1849
|
+
depth: int = Query(1, ge=1, le=5),
|
|
1850
|
+
limit: int = Query(200, ge=1, le=1000),
|
|
1851
|
+
_auth: str | None = Depends(verify_api_key),
|
|
1852
|
+
) -> dict[str, Any]:
|
|
1853
|
+
"""Traverse the knowledge graph from an entity."""
|
|
1854
|
+
from fastapi.responses import JSONResponse
|
|
1855
|
+
|
|
1856
|
+
mem = _get_memory()
|
|
1857
|
+
try:
|
|
1858
|
+
edges = mem.graph_neighbors(
|
|
1859
|
+
entity,
|
|
1860
|
+
relation=relation,
|
|
1861
|
+
direction=direction,
|
|
1862
|
+
min_confidence=min_confidence,
|
|
1863
|
+
depth=depth,
|
|
1864
|
+
limit=limit,
|
|
1865
|
+
)
|
|
1866
|
+
except Exception as exc:
|
|
1867
|
+
logger.warning("graph_neighbors failed for %s: %s", entity, exc)
|
|
1868
|
+
return JSONResponse(
|
|
1869
|
+
{"entity": entity, "edges": [], "count": 0, "error": str(exc)},
|
|
1870
|
+
status_code=200,
|
|
1871
|
+
)
|
|
1872
|
+
return {
|
|
1873
|
+
"entity": entity,
|
|
1874
|
+
"edges": [e.model_dump(mode="json") for e in edges],
|
|
1875
|
+
"count": len(edges),
|
|
1876
|
+
}
|
|
1877
|
+
|
|
1878
|
+
|
|
1683
1879
|
@app.get("/api/v1/pro/graph/expertise")
|
|
1684
1880
|
async def expertise_graph(
|
|
1685
1881
|
limit_agents: int = Query(30, ge=1, le=200),
|
|
@@ -1743,6 +1939,92 @@ async def expertise_graph(
|
|
|
1743
1939
|
}
|
|
1744
1940
|
|
|
1745
1941
|
|
|
1942
|
+
@app.post("/api/v1/pro/graph/backfill")
|
|
1943
|
+
async def graph_backfill(
|
|
1944
|
+
_auth: str | None = Depends(verify_api_key),
|
|
1945
|
+
) -> dict[str, Any]:
|
|
1946
|
+
"""Backfill knowledge graph edges from existing entries and outcomes.
|
|
1947
|
+
|
|
1948
|
+
Materializes edges from: pattern_refs → 'references' edges,
|
|
1949
|
+
outcome causal chains → 'informed' + 'read' edges,
|
|
1950
|
+
agent writes → 'wrote' edges.
|
|
1951
|
+
"""
|
|
1952
|
+
from amfs_core.models import GraphEdge
|
|
1953
|
+
|
|
1954
|
+
mem = _get_memory()
|
|
1955
|
+
entries = mem.list()
|
|
1956
|
+
outcomes = mem._adapter.list_outcomes(namespace=mem.namespace) if hasattr(mem._adapter, "list_outcomes") else []
|
|
1957
|
+
|
|
1958
|
+
created = 0
|
|
1959
|
+
errors = 0
|
|
1960
|
+
|
|
1961
|
+
for e in entries:
|
|
1962
|
+
aid = e.provenance.agent_id
|
|
1963
|
+
ep = e.entity_path
|
|
1964
|
+
ek = f"{ep}/{e.key}"
|
|
1965
|
+
|
|
1966
|
+
try:
|
|
1967
|
+
mem._adapter.upsert_graph_edge(
|
|
1968
|
+
GraphEdge(
|
|
1969
|
+
source_entity=aid,
|
|
1970
|
+
source_type="agent",
|
|
1971
|
+
relation="wrote",
|
|
1972
|
+
target_entity=ek,
|
|
1973
|
+
target_type="entry",
|
|
1974
|
+
confidence=e.confidence,
|
|
1975
|
+
provenance={"agent_id": aid, "trigger": "backfill"},
|
|
1976
|
+
),
|
|
1977
|
+
namespace=mem.namespace,
|
|
1978
|
+
branch=e.branch or "main",
|
|
1979
|
+
)
|
|
1980
|
+
created += 1
|
|
1981
|
+
except Exception:
|
|
1982
|
+
errors += 1
|
|
1983
|
+
|
|
1984
|
+
for ref in (e.provenance.pattern_refs or []):
|
|
1985
|
+
try:
|
|
1986
|
+
mem._adapter.upsert_graph_edge(
|
|
1987
|
+
GraphEdge(
|
|
1988
|
+
source_entity=ek,
|
|
1989
|
+
source_type="entry",
|
|
1990
|
+
relation="references",
|
|
1991
|
+
target_entity=ref,
|
|
1992
|
+
target_type="entry",
|
|
1993
|
+
provenance={"agent_id": aid, "trigger": "backfill"},
|
|
1994
|
+
),
|
|
1995
|
+
namespace=mem.namespace,
|
|
1996
|
+
branch=e.branch or "main",
|
|
1997
|
+
)
|
|
1998
|
+
created += 1
|
|
1999
|
+
except Exception:
|
|
2000
|
+
errors += 1
|
|
2001
|
+
|
|
2002
|
+
for o in outcomes:
|
|
2003
|
+
otype = getattr(o, "outcome_type", None)
|
|
2004
|
+
edge_conf = 1.0 if otype and otype.value == "success" else 0.7
|
|
2005
|
+
aid = getattr(o, "agent_id", "unknown")
|
|
2006
|
+
for ek in getattr(o, "causal_entry_keys", []):
|
|
2007
|
+
try:
|
|
2008
|
+
mem._adapter.upsert_graph_edge(
|
|
2009
|
+
GraphEdge(
|
|
2010
|
+
source_entity=ek,
|
|
2011
|
+
source_type="entry",
|
|
2012
|
+
relation="informed",
|
|
2013
|
+
target_entity=o.outcome_ref,
|
|
2014
|
+
target_type="outcome",
|
|
2015
|
+
confidence=edge_conf,
|
|
2016
|
+
provenance={"agent_id": aid, "trigger": "backfill"},
|
|
2017
|
+
),
|
|
2018
|
+
namespace=mem.namespace,
|
|
2019
|
+
branch="main",
|
|
2020
|
+
)
|
|
2021
|
+
created += 1
|
|
2022
|
+
except Exception:
|
|
2023
|
+
errors += 1
|
|
2024
|
+
|
|
2025
|
+
return {"edges_created": created, "errors": errors, "entries_scanned": len(entries), "outcomes_scanned": len(outcomes)}
|
|
2026
|
+
|
|
2027
|
+
|
|
1746
2028
|
# ──────────────────────────────────────────────────────────────────────
|
|
1747
2029
|
# Pro — HMO Memory Tiers
|
|
1748
2030
|
# ──────────────────────────────────────────────────────────────────────
|
|
@@ -1759,11 +2041,14 @@ def _compute_tiers(entries: list) -> tuple[dict[str, int], dict[str, float]]:
|
|
|
1759
2041
|
|
|
1760
2042
|
@app.get("/api/v1/pro/tiers/distribution")
|
|
1761
2043
|
async def tiers_distribution(
|
|
2044
|
+
agent_id: str | None = Query(None),
|
|
1762
2045
|
_auth: str | None = Depends(verify_api_key),
|
|
1763
2046
|
) -> dict[str, Any]:
|
|
1764
2047
|
"""Return HMO tier distribution (Hot / Warm / Archive)."""
|
|
1765
2048
|
mem = _get_memory()
|
|
1766
2049
|
entries = mem.list()
|
|
2050
|
+
if agent_id:
|
|
2051
|
+
entries = [e for e in entries if e.provenance.agent_id == agent_id]
|
|
1767
2052
|
|
|
1768
2053
|
tiers, scores = _compute_tiers(entries)
|
|
1769
2054
|
|
|
@@ -1795,11 +2080,14 @@ async def tiers_distribution(
|
|
|
1795
2080
|
async def tiers_entries(
|
|
1796
2081
|
tier: int = Query(..., ge=1, le=3),
|
|
1797
2082
|
limit: int = Query(50, ge=1, le=500),
|
|
2083
|
+
agent_id: str | None = Query(None),
|
|
1798
2084
|
_auth: str | None = Depends(verify_api_key),
|
|
1799
2085
|
) -> list[dict[str, Any]]:
|
|
1800
2086
|
"""Return entries for a given HMO tier as a flat array."""
|
|
1801
2087
|
mem = _get_memory()
|
|
1802
2088
|
entries = mem.list()
|
|
2089
|
+
if agent_id:
|
|
2090
|
+
entries = [e for e in entries if e.provenance.agent_id == agent_id]
|
|
1803
2091
|
|
|
1804
2092
|
tier_map, score_map = _compute_tiers(entries)
|
|
1805
2093
|
|
|
@@ -14,11 +14,16 @@ logger = logging.getLogger(__name__)
|
|
|
14
14
|
def apply_tenant_headers_from_request(request: Request) -> None:
|
|
15
15
|
"""If proxy secret + account id headers match env, set thread-local tenant for DB RLS."""
|
|
16
16
|
try:
|
|
17
|
-
from amfs_postgres.tenant_context import
|
|
17
|
+
from amfs_postgres.tenant_context import (
|
|
18
|
+
get_request_tenant_account_id,
|
|
19
|
+
set_tls_tenant_account_id,
|
|
20
|
+
)
|
|
18
21
|
except ImportError:
|
|
19
22
|
return
|
|
20
23
|
|
|
21
|
-
|
|
24
|
+
if get_request_tenant_account_id() is not None:
|
|
25
|
+
return
|
|
26
|
+
|
|
22
27
|
secret = os.environ.get("AMFS_DASHBOARD_PROXY_SECRET", "")
|
|
23
28
|
if not secret:
|
|
24
29
|
return
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|