{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "f59ca99f",
   "metadata": {},
   "source": [
    "# Using the Hub\n",
    "\n",
    "The Hub loads a module export straight from its git repository at runtime.\n",
    "`hub.use(\"org/name:Export\")` returns the actual object — a Flow class, a\n",
    "function, a type — with no PyPI wheel and no in-repo paths to reverse-engineer.\n",
    "This notebook resolves the **live** public module `openretriever/hello-world`,\n",
    "so every output below is captured from a real networked call. The only\n",
    "requirement beyond `retriever-core` is a network connection when you run it."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "af710f3b",
   "metadata": {},
   "source": [
    "> **Running in Colab?** The next cell installs `retriever-core`. From a source\n",
    "> checkout (or once it's already installed) the install is skipped."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "44d15152",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Colab setup: install retriever-core only if it isn't importable yet.\n",
    "try:\n",
    "    import retriever  # noqa: F401\n",
    "except ImportError:  # pragma: no cover\n",
    "    import subprocess\n",
    "    import sys\n",
    "\n",
    "    subprocess.run(\n",
    "        [sys.executable, \"-m\", \"pip\", \"install\", \"retriever-core\"], check=True\n",
    "    )"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "30d3a028",
   "metadata": {},
   "source": [
    "## Load one export by its ref\n",
    "\n",
    "A ref is `{org}/{name}:{Export}`. Here `openretriever/hello-world` is the\n",
    "module and `HelloFlow` is one name from its export table. `hub.use` fetches the\n",
    "repo, imports it under a private namespace, and hands back the class itself —\n",
    "so you construct it and call `step()` exactly like a local Flow."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fae324d0",
   "metadata": {},
   "outputs": [],
   "source": [
    "from retriever import hub\n",
    "\n",
    "HelloFlow = hub.use(\"openretriever/hello-world:HelloFlow\")\n",
    "\n",
    "flow = HelloFlow()\n",
    "print(\"returned:\", HelloFlow.__name__)\n",
    "print(\"step:\", flow.step(None).text)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7ed00b9a",
   "metadata": {},
   "source": [
    "The object is real, not a proxy: `HelloFlow()` builds an instance and\n",
    "`step(None)` runs it in-process. Nothing here needed a backend or a clock."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "64698a16",
   "metadata": {},
   "source": [
    "## An export can be any Python object\n",
    "\n",
    "The export table is not limited to Flows. A module can publish plain functions,\n",
    "types, or values under the same ref shape. `greet` is a function export — load\n",
    "it and call it directly."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3ee99bcb",
   "metadata": {},
   "outputs": [],
   "source": [
    "greet = hub.use(\"openretriever/hello-world:greet\")\n",
    "print(greet(\"retriever\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "114b5fe8",
   "metadata": {},
   "source": [
    "## Load the whole module as a proxy\n",
    "\n",
    "Drop the `:attribute` and `hub.use` returns a `ModuleProxy` over the declared\n",
    "export table. `dir(proxy)` lists the exports and its repr names them, so the\n",
    "manifest — not the file tree — is the module's public surface."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1516b840",
   "metadata": {},
   "outputs": [],
   "source": [
    "mod = hub.use(\"openretriever/hello-world\")\n",
    "print(\"repr:\", repr(mod))\n",
    "print(\"exports:\", dir(mod))\n",
    "print(\"via proxy:\", mod.HelloFlow().step(None).text)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d4bb43b1",
   "metadata": {},
   "source": [
    "A proxy exposes *only* declared exports. Reaching for a name that is not in the\n",
    "manifest raises `AttributeError` that lists what is actually available."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e3573508",
   "metadata": {},
   "outputs": [],
   "source": [
    "try:\n",
    "    mod.SecretFlow\n",
    "except AttributeError as exc:\n",
    "    print(\"AttributeError:\", exc)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "07dbc6f9",
   "metadata": {},
   "source": [
    "## The resolution chain, once\n",
    "\n",
    "Given a ref, the loader walks a fixed chain:\n",
    "\n",
    "1. Parse `{org}/{name}[:attribute][@version]`.\n",
    "2. Look up `{org}/{name}` in the Hub **index** to get the module's git repo URL.\n",
    "3. Resolve the version to a commit — newest semver tag, or the tag you pinned.\n",
    "4. Download that commit's tarball and cache it under `~/.retriever/hub/cache/`.\n",
    "5. Read `[tool.retriever.module]` from the repo's `pyproject.toml` for the\n",
    "   export table, then check `min_retriever_version` and declared deps.\n",
    "6. Import the package under a private, commit-scoped namespace and return the\n",
    "   requested export.\n",
    "\n",
    "The result is cached in-process, so a second `hub.use` of the same ref returns\n",
    "the *same* object without re-fetching."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "723ad9b9",
   "metadata": {},
   "outputs": [],
   "source": [
    "first = hub.use(\"openretriever/hello-world:HelloFlow\")\n",
    "second = hub.use(\"openretriever/hello-world:HelloFlow\")\n",
    "print(\"same object cached in-process:\", first is second)\n",
    "\n",
    "from retriever.hub._ref import parse_ref\n",
    "\n",
    "ref = parse_ref(\"openretriever/hello-world:HelloFlow\")\n",
    "print(\"parsed ->\", f\"org={ref.org} name={ref.name} attr={ref.attribute} version={ref.version}\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
