Files
wehub-resource-sync bcbd1bdb22
Integ / changes (push) Has been skipped
Pre-commit / pre-commit (push) Failing after 1s
CLI exit codes / changes (push) Has been skipped
Test (Install) / changes (push) Has been skipped
Test (Python) / changes (push) Has been skipped
Test (TypeScript) / changes (push) Has been skipped
CLI exit codes / cli-gate (push) Has been cancelled
Test (Install) / test-install-gate (push) Has been cancelled
Integ / integ-gate (push) Has been cancelled
Test (Python) / test-python-gate (push) Has been cancelled
Test (TypeScript) / test-typescript-gate (push) Has been cancelled
Test (Install) / python-minimal (3.12) (push) Has been cancelled
Test (Install) / python-minimal (3.11) (push) Has been cancelled
Test (Install) / python-extra (agno, mirage.agents.agno) (push) Has been cancelled
Test (Install) / python-extra (chroma, mirage.resource.chroma) (push) Has been cancelled
Test (Install) / python-extra (pdf, mirage.core.filetype.pdf) (push) Has been cancelled
Integ / integ (push) Has been cancelled
Integ / integ-database (push) Has been cancelled
Integ / integ-database-ts (push) Has been cancelled
Integ / integ-data (push) Has been cancelled
Integ / integ-ssh (push) Has been cancelled
Integ / integ-ssh-ts (push) Has been cancelled
Test (Python) / audit (push) Has been cancelled
Test (TypeScript) / test (push) Has been cancelled
Test (TypeScript) / python-fs-shim (push) Has been cancelled
CLI exit codes / Python CLI (push) Has been cancelled
CLI exit codes / TypeScript CLI (push) Has been cancelled
CLI exit codes / Cross-language snapshot interop (push) Has been cancelled
Test (Python) / test (push) Has been cancelled
Test (Python) / import-isolation (deepagents, openai, mirage.agents.openai_agents) (push) Has been cancelled
Test (Python) / import-isolation (deepagents, pydantic-ai, mirage.agents.pydantic_ai) (push) Has been cancelled
Integ / integ-ts (push) Has been cancelled
Integ / integ-fuse (push) Has been cancelled
Test (Install) / python-extra (databricks, mirage.resource.databricks_volume) (push) Has been cancelled
Test (Install) / python-extra (deepagents, mirage.agents.langchain) (push) Has been cancelled
Test (Install) / python-extra (email, mirage.resource.email) (push) Has been cancelled
Test (Install) / python-extra (fuse, mirage.fuse.mount) (push) Has been cancelled
Test (Install) / python-extra (hdf5, mirage.core.filetype.hdf5) (push) Has been cancelled
Test (Install) / python-extra (hf, mirage.resource.hf_buckets) (push) Has been cancelled
Test (Install) / python-extra (lancedb, mirage.resource.lancedb) (push) Has been cancelled
Test (Install) / python-extra (langfuse, mirage.resource.langfuse) (push) Has been cancelled
Test (Install) / python-extra (mongodb, mirage.resource.mongodb) (push) Has been cancelled
Test (Install) / python-extra (nextcloud, mirage.resource.nextcloud) (push) Has been cancelled
Test (Install) / python-extra (openai, mirage.agents.openai_agents) (push) Has been cancelled
Test (Install) / python-extra (openhands, mirage.agents.openhands, 3.12) (push) Has been cancelled
Test (Install) / python-extra (parquet, mirage.core.filetype.parquet) (push) Has been cancelled
Test (Install) / python-extra (postgres, mirage.resource.postgres) (push) Has been cancelled
Test (Install) / python-extra (pydantic-ai, mirage.agents.pydantic_ai) (push) Has been cancelled
Test (Install) / python-extra (qdrant, mirage.resource.qdrant) (push) Has been cancelled
Test (Install) / python-extra (redis, mirage.resource.redis) (push) Has been cancelled
Test (Install) / python-extra (s3, mirage.resource.s3) (push) Has been cancelled
Test (Install) / python-extra (ssh, mirage.resource.ssh) (push) Has been cancelled
Test (Install) / ts-minimal (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:30:44 +08:00

342 lines
13 KiB
Python

# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. =========
import asyncio
import json
import re
import threading
from collections import Counter
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from urllib.parse import urlparse
from cases import run_not_found, run_provision_probe, run_sed_readonly_probe
from mirage import MountMode, Workspace
from mirage.resource.dify import DifyConfig, DifyResource
DATASET_ID = "kb-7f3a"
MOUNT = "/knowledge/"
FIXED_UPDATED_AT = 1716282000
# Flat document list mirroring a Dify dataset, as (id, name, slug, size,
# segments) tuples. The slug maps a document to a virtual path; CHANGELOG.md
# has no slug and falls back to its name. Segments are the chunks Dify embeds;
# reading a file rejoins them with newlines, so segment order is line order.
DOCS: list[tuple[str, str, str | None, int, list[str]]] = [
("doc-quickstart", "Quickstart", "guides/quickstart", 180, [
"Welcome to Acme. This quickstart gets you running fast.",
"Install the CLI with npm i -g acme then run acme login.",
"Set your token in the ACME_TOKEN environment variable.",
]),
("doc-auth", "Authentication", "guides/auth", 190, [
"Authentication uses bearer tokens via the Authorization header.",
"Requests are rate limited to 100 calls per minute per token.",
"If you exceed the limit you receive HTTP 429 and must back off.",
]),
("doc-refunds", "Refund policy", "policies/refunds", 150, [
"Refunds are available within 30 days of purchase.",
"Email support to start a refund with your order id.",
"Approved refunds are processed within five business days.",
]),
("doc-privacy", "Privacy policy", "policies/privacy", 120, [
"Customer data is stored encrypted at rest and in transit.",
"You may request deletion of your data at any time.",
]),
("doc-changelog", "CHANGELOG.md", None, 90, [
"v2.0 added rate limit headers and refund automation.",
"v1.5 introduced encrypted data exports.",
]),
]
DOC_BY_ID = {doc[0]: doc for doc in DOCS}
# Per-route hit counter so the index fast-path is observable: the first listing
# issues one GET /documents and populates the RAM index, after which a second
# listing reuses the index (list stays at 1). cat/grep/search are the commands
# that hit the network again.
API_CALLS: Counter = Counter()
def _document_summary(doc: tuple) -> dict:
doc_id, name, slug, size, _segments = doc
metadata = []
if slug is not None:
metadata = [{"name": "slug", "value": slug}]
return {
"id": doc_id,
"name": name,
"doc_metadata": metadata,
"enabled": True,
"indexing_status": "completed",
"archived": False,
"tokens": 8,
"data_source_type": "upload_file",
"data_source_detail_dict": {
"upload_file": {
"size": size
}
},
"created_at": FIXED_UPDATED_AT,
}
def _document_detail(doc: tuple) -> dict:
detail = _document_summary(doc)
detail["updated_at"] = FIXED_UPDATED_AT
return detail
def _build_record(doc_id: str, content: str, score: float) -> dict:
doc = DOC_BY_ID[doc_id]
return {
"segment": {
"id": f"{doc_id}:{score:.2f}",
"document_id": doc_id,
"content": content,
"document": {
"id": doc[0],
"data_source_type": "upload_file",
"name": doc[1],
"doc_type": None,
"doc_metadata": _document_summary(doc)["doc_metadata"],
},
},
"child_chunks": [],
"score": score,
"tsne_position": None,
"files": [],
"summary": None,
}
def _retrieve_records(query: str) -> list[dict]:
lowered = query.lower()
if "throttl" in lowered or "rate" in lowered or "429" in lowered:
return [
_build_record(
"doc-auth",
"Requests are rate limited to 100 calls per minute per token.",
0.92,
),
_build_record(
"doc-auth",
("If you exceed the limit you receive HTTP 429 and must "
"back off."),
0.88,
),
]
elif "refund" in lowered or "money" in lowered:
return [
_build_record(
"doc-refunds",
"Refunds are available within 30 days of purchase.",
0.91,
),
_build_record(
"doc-refunds",
"Approved refunds are processed within five business days.",
0.84,
),
]
elif "encrypt" in lowered or "privacy" in lowered:
return [
_build_record(
"doc-privacy",
"Customer data is stored encrypted at rest and in transit.",
0.89,
)
]
return []
class DifyMockHandler(BaseHTTPRequestHandler):
def log_message(self, *args: object) -> None:
pass
def _send_json(self, payload: dict) -> None:
body = json.dumps(payload).encode()
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
def do_GET(self) -> None:
path = urlparse(self.path).path
segments = re.match(r"^/datasets/[^/]+/documents/([^/]+)/segments$",
path)
if segments is not None:
API_CALLS["segments"] += 1
doc = DOC_BY_ID[segments.group(1)]
self._send_json({
"data": [{
"content": content
} for content in doc[4]],
"has_more":
False,
})
return
detail = re.match(r"^/datasets/[^/]+/documents/([^/]+)$", path)
if detail is not None:
API_CALLS["detail"] += 1
self._send_json(_document_detail(DOC_BY_ID[detail.group(1)]))
return
if re.match(r"^/datasets/[^/]+/documents$", path) is not None:
API_CALLS["list"] += 1
self._send_json({
"data": [_document_summary(doc) for doc in DOCS],
"has_more": False,
})
return
self.send_error(404)
def do_POST(self) -> None:
path = urlparse(self.path).path
if re.match(r"^/datasets/[^/]+/retrieve$", path) is not None:
API_CALLS["retrieve"] += 1
length = int(self.headers.get("Content-Length", 0))
body = json.loads(self.rfile.read(length) or b"{}")
self._send_json(
{"records": _retrieve_records(body.get("query") or "")})
return
self.send_error(404)
# Read-only, deterministic commands exercising path resolution, segment
# reassembly, stat detail, and recursive grep. {root} is the mount root.
PER_MOUNT_CASES: list[tuple[str, str]] = [
("ls", "ls {root}"),
("ls_guides", "ls {root}guides/"),
("tree", "tree {root}"),
("find_md", "find {root} -name '*.md'"),
("find_type_f", "find {root} -type f | sort"),
("find_root_maxdepth0", "find {root} -maxdepth 0"),
("find_root_name", "find {root} -name knowledge"),
("stat_fmt", "stat -c '%s %n' {root}guides/auth"),
("cat_auth", "cat {root}guides/auth"),
("head_1", "head -n 1 {root}guides/quickstart"),
("tail_1", "tail -n 1 {root}guides/quickstart"),
("wc_l", "wc -l {root}guides/auth"),
("wc_c", "wc -c {root}policies/refunds"),
("grep_429", "grep 429 {root}guides/auth"),
("grep_c_rate", "grep -c rate {root}guides/auth"),
("grep_e_multi", "grep -e 429 -e rate {root}guides/auth"),
("grep_r_refund", "grep -r refund {root}policies/"),
("grep_rl_encrypted", "grep -rl encrypted {root}"),
("rg_l_token", "rg -l token {root}"),
("pipe_sort_uniq_wc", "cat {root}guides/auth | sort | uniq | wc -l"),
("pipe_sort_uniq_w0_wc",
"cat {root}guides/auth | sort | uniq -w 0 | wc -l"),
]
# Dify-native semantic retrieval mirroring examples/python/dify/dify.py. The
# first two run unscoped against the mount root; the third scopes to a folder,
# which the backend turns into a slug metadata filter on the /retrieve call.
SEARCH_CASES: list[tuple[str, str]] = [
("search_throttle", 'search "how am I throttled" {root}'),
("search_scoped_refund", 'search "money back" {root}policies'),
("search_hybrid_encrypted",
"search --method hybrid --top-k 3 encrypted {root}"),
]
# Index fast-path accounting: run from a fresh workspace and count backend
# calls per route. readdir-driven listing populates the index, so a second
# listing issues zero extra GET /documents calls; cat is the first command to
# hit the segments endpoint.
ACCOUNTING_CASES: list[tuple[str, list[str]]] = [
("calls_ls", ["ls {root}"]),
("calls_ls_then_guides", ["ls {root}", "ls {root}guides/"]),
("calls_cat", ["cat {root}guides/auth"]),
]
# Warm-read coherence: a cat warms the file cache for guides/auth (one segments
# fetch), then each read-only command reads the same file and must be served
# from cache, leaving the segments call count unchanged. cat reads the document
# directly; grep/head/tail/wc funnel through the shared generic consumers. A
# regression in cache-serving would bump segments_after above segments_before.
WARM_READ_CASES: list[tuple[str, str]] = [
("warm_cat", "cat {root}guides/auth"),
("warm_grep", "grep token {root}guides/auth"),
("warm_head", "head -n 1 {root}guides/auth"),
("warm_tail", "tail -n 1 {root}guides/auth"),
("warm_wc", "wc -l {root}guides/auth"),
]
def _build_workspace(base_url: str) -> Workspace:
resource = DifyResource(
DifyConfig(api_key="testing", base_url=base_url,
dataset_id=DATASET_ID))
return Workspace({MOUNT: resource}, mode=MountMode.READ)
async def _run(ws: Workspace, name: str, cmd: str) -> None:
result = await ws.execute(cmd)
out = await result.stdout_str()
print(f"=== {name} ===")
print(out, end="" if out.endswith("\n") else "\n")
async def _measure_calls(base_url: str, name: str, cmds: list[str]) -> None:
ws = _build_workspace(base_url)
API_CALLS.clear()
for cmd in cmds:
await ws.execute(cmd.format(root=MOUNT))
print(f"=== {name} ===")
print(f"list={API_CALLS.get('list', 0)} "
f"detail={API_CALLS.get('detail', 0)} "
f"segments={API_CALLS.get('segments', 0)} "
f"retrieve={API_CALLS.get('retrieve', 0)}")
async def _warm_read(base_url: str, name: str, cmd: str) -> None:
ws = _build_workspace(base_url)
API_CALLS.clear()
warmup = f"cat {MOUNT}guides/auth"
await (await ws.execute(warmup)).stdout_str()
warm_segments = API_CALLS.get("segments", 0)
await (await ws.execute(cmd.format(root=MOUNT))).stdout_str()
after_segments = API_CALLS.get("segments", 0)
print(f"=== {name} ===")
print(f"segments_before={warm_segments} segments_after={after_segments} "
f"served_from_cache={after_segments == warm_segments}")
async def main() -> None:
server = ThreadingHTTPServer(("127.0.0.1", 0), DifyMockHandler)
thread = threading.Thread(target=server.serve_forever, daemon=True)
thread.start()
host, port = server.server_address
base_url = f"http://{host}:{port}"
try:
ws = _build_workspace(base_url)
for name, tmpl in PER_MOUNT_CASES:
await _run(ws, name, tmpl.format(root=MOUNT))
for name, tmpl in SEARCH_CASES:
await _run(ws, name, tmpl.format(root=MOUNT))
for name, cmds in ACCOUNTING_CASES:
await _measure_calls(base_url, name, cmds)
for name, tmpl in WARM_READ_CASES:
await _warm_read(base_url, name, tmpl)
await run_not_found(ws, MOUNT)
await run_provision_probe(ws, f"{MOUNT}guides/auth")
await run_sed_readonly_probe(ws, f"{MOUNT}guides/auth")
finally:
server.shutdown()
if __name__ == "__main__":
asyncio.run(main())