From b1d03b8713b811e01fdd65a345e395117d8cfdb7 Mon Sep 17 00:00:00 2001 From: Tim Volker Date: Mon, 1 Jun 2026 09:16:35 -0700 Subject: [PATCH] Speed up cached devcontainer responses --- helpers/github_helpers.py | 9 +- main.py | 10 +- tests/test_cached_database_short_circuit.py | 159 ++++++++++++++++++++ tests/test_check_url_exists_query.py | 74 +++++++++ 4 files changed, 246 insertions(+), 6 deletions(-) create mode 100644 tests/test_cached_database_short_circuit.py create mode 100644 tests/test_check_url_exists_query.py diff --git a/helpers/github_helpers.py b/helpers/github_helpers.py index 11760d5..38b7e24 100644 --- a/helpers/github_helpers.py +++ b/helpers/github_helpers.py @@ -145,6 +145,13 @@ def traverse_dir(api_url, depth=0, prefix=""): return "\n\n".join(context), existing_devcontainer, devcontainer_url def check_url_exists(url): - existing = supabase.table("devcontainers").select("*").eq("url", url).order("created_at", desc=True).limit(1).execute() + existing = ( + supabase.table("devcontainers") + .select("devcontainer_json,generated,devcontainer_url,created_at") + .eq("url", url) + .order("created_at", desc=True) + .limit(1) + .execute() + ) existing_record = existing.data[0] if existing.data else None return existing_record is not None, existing_record \ No newline at end of file diff --git a/main.py b/main.py index 2e6410d..7944a21 100644 --- a/main.py +++ b/main.py @@ -112,17 +112,17 @@ async def post(repo_url: str, regenerate: bool = False): exists, existing_record = check_url_exists(repo_url) logging.info(f"URL check result: exists={exists}, existing_record={existing_record}") - repo_context, existing_devcontainer, devcontainer_url = fetch_repo_context(repo_url) - logging.info(f"Fetched repo context. Existing devcontainer: {'Yes' if existing_devcontainer else 'No'}") - logging.info(f"Devcontainer URL: {devcontainer_url}") - - if exists and not regenerate: + if exists and existing_record and not regenerate: logging.info(f"URL already exists in database. Returning existing devcontainer_json for: {repo_url}") devcontainer_json = existing_record['devcontainer_json'] generated = existing_record['generated'] source = "database" url = existing_record['devcontainer_url'] else: + repo_context, existing_devcontainer, devcontainer_url = fetch_repo_context(repo_url) + logging.info(f"Fetched repo context. Existing devcontainer: {'Yes' if existing_devcontainer else 'No'}") + logging.info(f"Devcontainer URL: {devcontainer_url}") + devcontainer_json, url = generate_devcontainer_json(instructor_client, repo_url, repo_context, devcontainer_url, regenerate=regenerate) generated = True source = "generated" if url is None else "repository" diff --git a/tests/test_cached_database_short_circuit.py b/tests/test_cached_database_short_circuit.py new file mode 100644 index 0000000..6b9b282 --- /dev/null +++ b/tests/test_cached_database_short_circuit.py @@ -0,0 +1,159 @@ +import asyncio +import importlib.util +import sys +import types +from pathlib import Path + + +def _tag(name): + def render(*children, **attrs): + return {"tag": name, "children": children, "attrs": attrs} + + return render + + +def load_main_module(monkeypatch): + """Import main.py with lightweight stand-ins for optional app dependencies.""" + repo_root = Path(__file__).resolve().parents[1] + + for module_name in [ + "main_under_test", + "fasthtml", + "fasthtml.common", + "fastcore", + "fastcore.xtras", + "content", + "supabase_client", + "helpers.openai_helpers", + "helpers.github_helpers", + "helpers.devcontainer_helpers", + "helpers.token_helpers", + ]: + sys.modules.pop(module_name, None) + + common = types.ModuleType("fasthtml.common") + for name in [ + "Script", + "Meta", + "Link", + "Title", + "Main", + "Div", + "Article", + "Pre", + "Code", + "Button", + "Img", + "Span", + "H2", + "P", + "FileResponse", + ]: + setattr(common, name, _tag(name)) + + common.picolink = _tag("picolink") + common.scopesrc = _tag("scopesrc") + common.Favicon = lambda *args, **kwargs: (_tag("Favicon")(*args, **kwargs),) + common.Socials = lambda *args, **kwargs: (_tag("Socials")(*args, **kwargs),) + common.serve = lambda *args, **kwargs: None + + def fast_app(*args, **kwargs): + def rt(*route_args, **route_kwargs): + def decorator(func): + return func + + return decorator + + return object(), rt + + common.fast_app = fast_app + fasthtml = types.ModuleType("fasthtml") + fasthtml.common = common + monkeypatch.setitem(sys.modules, "fasthtml", fasthtml) + monkeypatch.setitem(sys.modules, "fasthtml.common", common) + + xtras = types.ModuleType("fastcore.xtras") + xtras.timed_cache = lambda seconds=60: (lambda func: func) + fastcore = types.ModuleType("fastcore") + fastcore.xtras = xtras + monkeypatch.setitem(sys.modules, "fastcore", fastcore) + monkeypatch.setitem(sys.modules, "fastcore.xtras", xtras) + + content = types.ModuleType("content") + content.description = "test description" + content.picolink = _tag("picolink") + content.scopesrc = _tag("scopesrc") + for name in [ + "hero_section", + "generator_section", + "setup_section", + "manifesto", + "benefits_section", + "examples_section", + "faq_section", + "cta_section", + "footer_section", + "manifesto_page", + ]: + setattr(content, name, _tag(name)) + monkeypatch.setitem(sys.modules, "content", content) + + supabase_client = types.ModuleType("supabase_client") + supabase_client.supabase = object() + monkeypatch.setitem(sys.modules, "supabase_client", supabase_client) + + openai_helpers = types.ModuleType("helpers.openai_helpers") + openai_helpers.setup_azure_openai = lambda: object() + openai_helpers.setup_instructor = lambda client: object() + monkeypatch.setitem(sys.modules, "helpers.openai_helpers", openai_helpers) + + github_helpers = types.ModuleType("helpers.github_helpers") + github_helpers.fetch_repo_context = lambda repo_url: ("repo context", None, None) + github_helpers.check_url_exists = lambda repo_url: (False, None) + monkeypatch.setitem(sys.modules, "helpers.github_helpers", github_helpers) + + devcontainer_helpers = types.ModuleType("helpers.devcontainer_helpers") + devcontainer_helpers.generate_devcontainer_json = lambda *args, **kwargs: ('{}', None) + devcontainer_helpers.validate_devcontainer_json = lambda devcontainer_json: True + monkeypatch.setitem(sys.modules, "helpers.devcontainer_helpers", devcontainer_helpers) + + token_helpers = types.ModuleType("helpers.token_helpers") + token_helpers.count_tokens = lambda text: len(text.split()) + token_helpers.truncate_to_token_limit = lambda text, *args, **kwargs: text + monkeypatch.setitem(sys.modules, "helpers.token_helpers", token_helpers) + + spec = importlib.util.spec_from_file_location("main_under_test", repo_root / "main.py") + module = importlib.util.module_from_spec(spec) + monkeypatch.setitem(sys.modules, "main_under_test", module) + spec.loader.exec_module(module) + return module + + +def test_cached_database_result_skips_repo_fetch(monkeypatch): + main = load_main_module(monkeypatch) + calls = {"fetch_repo_context": 0, "generate_devcontainer_json": 0} + + cached_record = { + "devcontainer_json": '{"name": "cached-devcontainer"}', + "generated": True, + "devcontainer_url": "https://example.com/.devcontainer/devcontainer.json", + } + + monkeypatch.setattr(main, "check_url_exists", lambda repo_url: (True, cached_record)) + + def fetch_repo_context(repo_url): + calls["fetch_repo_context"] += 1 + return "repo context", None, None + + def generate_devcontainer_json(*args, **kwargs): + calls["generate_devcontainer_json"] += 1 + return '{"name": "generated-devcontainer"}', None + + monkeypatch.setattr(main, "fetch_repo_context", fetch_repo_context) + monkeypatch.setattr(main, "generate_devcontainer_json", generate_devcontainer_json) + + result = asyncio.run(main.post("https://github.com/daytonaio/devcontainer-generator/")) + + assert calls == {"fetch_repo_context": 0, "generate_devcontainer_json": 0} + assert result["tag"] == "Div" + assert "database" in result["children"][0]["children"][0] diff --git a/tests/test_check_url_exists_query.py b/tests/test_check_url_exists_query.py new file mode 100644 index 0000000..423f771 --- /dev/null +++ b/tests/test_check_url_exists_query.py @@ -0,0 +1,74 @@ +import importlib +import sys +import types + + +class FakeQuery: + def __init__(self): + self.selected_columns = None + self.filters = [] + self.order_args = None + self.limit_value = None + + def select(self, columns): + self.selected_columns = columns + return self + + def eq(self, column, value): + self.filters.append((column, value)) + return self + + def order(self, column, desc=False): + self.order_args = (column, desc) + return self + + def limit(self, value): + self.limit_value = value + return self + + def execute(self): + return types.SimpleNamespace( + data=[ + { + "devcontainer_json": '{"name": "cached"}', + "generated": True, + "devcontainer_url": "https://example.com/devcontainer.json", + "created_at": "2024-01-01T00:00:00", + } + ] + ) + + +class FakeSupabase: + def __init__(self): + self.query = FakeQuery() + self.table_name = None + + def table(self, table_name): + self.table_name = table_name + return self.query + + +def test_check_url_exists_selects_only_fields_needed_for_cached_response(monkeypatch): + fake_supabase = FakeSupabase() + + supabase_client = types.ModuleType("supabase_client") + supabase_client.supabase = fake_supabase + monkeypatch.setitem(sys.modules, "supabase_client", supabase_client) + + token_helpers = types.ModuleType("helpers.token_helpers") + token_helpers.count_tokens = lambda text: len(text.split()) + monkeypatch.setitem(sys.modules, "helpers.token_helpers", token_helpers) + + sys.modules.pop("helpers.github_helpers", None) + github_helpers = importlib.import_module("helpers.github_helpers") + + exists, record = github_helpers.check_url_exists("https://github.com/daytonaio/devcontainer-generator") + + assert exists is True + assert record["devcontainer_json"] == '{"name": "cached"}' + assert fake_supabase.table_name == "devcontainers" + assert fake_supabase.query.selected_columns == "devcontainer_json,generated,devcontainer_url,created_at" + assert fake_supabase.query.filters == [("url", "https://github.com/daytonaio/devcontainer-generator")] + assert fake_supabase.query.order_args == ("created_at", True) + assert fake_supabase.query.limit_value == 1