hyperpocket 0.3.7__py3-none-any.whl → 0.4.0__py3-none-any.whl
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.
- hyperpocket/auth/provider.py +1 -0
- hyperpocket/auth/weaviate/context.py +12 -0
- hyperpocket/auth/weaviate/token_context.py +11 -0
- hyperpocket/auth/weaviate/token_handler.py +68 -0
- hyperpocket/auth/weaviate/token_schema.py +7 -0
- hyperpocket/cli/eject.py +2 -7
- hyperpocket/cli/pull.py +2 -7
- hyperpocket/config/settings.py +2 -1
- hyperpocket/pocket_core.py +41 -68
- hyperpocket/pocket_main.py +37 -16
- hyperpocket/repository/__init__.py +3 -4
- hyperpocket/repository/repository.py +6 -41
- hyperpocket/repository/tool_reference.py +28 -0
- hyperpocket/server/auth/weaviate.py +27 -0
- hyperpocket/server/server.py +127 -61
- hyperpocket/session/in_memory.py +13 -3
- hyperpocket/tool/__init__.py +0 -3
- hyperpocket/tool/dock/__init__.py +3 -0
- hyperpocket/tool/dock/dock.py +34 -0
- hyperpocket/tool/function/__init__.py +1 -1
- hyperpocket/tool/function/tool.py +62 -32
- hyperpocket/tool/tool.py +1 -9
- hyperpocket/tool_like.py +2 -1
- hyperpocket/util/generate_slug.py +4 -0
- hyperpocket/util/json_schema_to_model.py +5 -1
- {hyperpocket-0.3.7.dist-info → hyperpocket-0.4.0.dist-info}/METADATA +4 -1
- {hyperpocket-0.3.7.dist-info → hyperpocket-0.4.0.dist-info}/RECORD +30 -36
- hyperpocket/cli/sync.py +0 -17
- hyperpocket/repository/lock.py +0 -240
- hyperpocket/repository/lockfile.py +0 -62
- hyperpocket/server/tool/__init__.py +0 -10
- hyperpocket/server/tool/dto/script.py +0 -33
- hyperpocket/server/tool/wasm.py +0 -46
- hyperpocket/tool/wasm/README.md +0 -166
- hyperpocket/tool/wasm/__init__.py +0 -3
- hyperpocket/tool/wasm/browser.py +0 -63
- hyperpocket/tool/wasm/invoker.py +0 -41
- hyperpocket/tool/wasm/script.py +0 -134
- hyperpocket/tool/wasm/templates/__init__.py +0 -35
- hyperpocket/tool/wasm/templates/node.py +0 -87
- hyperpocket/tool/wasm/templates/python.py +0 -93
- hyperpocket/tool/wasm/tool.py +0 -163
- /hyperpocket/{server/tool/dto → auth/weaviate}/__init__.py +0 -0
- {hyperpocket-0.3.7.dist-info → hyperpocket-0.4.0.dist-info}/WHEEL +0 -0
- {hyperpocket-0.3.7.dist-info → hyperpocket-0.4.0.dist-info}/entry_points.txt +0 -0
    
        hyperpocket/auth/provider.py
    CHANGED
    
    
| @@ -0,0 +1,12 @@ | |
| 1 | 
            +
             | 
| 2 | 
            +
            from hyperpocket.auth.context import AuthContext
         | 
| 3 | 
            +
            class WeaviateAuthContext(AuthContext):
         | 
| 4 | 
            +
                _ACCESS_TOKEN_KEY: str = "WEAVIATE_TOKEN"
         | 
| 5 | 
            +
                def to_dict(self) -> dict[str, str]:
         | 
| 6 | 
            +
                    return {
         | 
| 7 | 
            +
                        self._ACCESS_TOKEN_KEY: self.access_token,
         | 
| 8 | 
            +
                    }
         | 
| 9 | 
            +
                def to_profiled_dict(self, profile: str) -> dict[str, str]:
         | 
| 10 | 
            +
                    return {
         | 
| 11 | 
            +
                        f"{profile.upper()}_{self._ACCESS_TOKEN_KEY}": self.access_token,
         | 
| 12 | 
            +
                    }
         | 
| @@ -0,0 +1,11 @@ | |
| 1 | 
            +
            from hyperpocket.auth.weaviate.context import WeaviateAuthContext
         | 
| 2 | 
            +
            from hyperpocket.auth.weaviate.token_schema import WeaviateTokenResponse
         | 
| 3 | 
            +
            class WeaviateTokenAuthContext(WeaviateAuthContext):
         | 
| 4 | 
            +
                @classmethod
         | 
| 5 | 
            +
                def from_weaviate_token_response(cls, response: WeaviateTokenResponse):
         | 
| 6 | 
            +
                    description = f'Weaviate Token Context logged in'
         | 
| 7 | 
            +
                    return cls(
         | 
| 8 | 
            +
                        access_token=response.access_token,
         | 
| 9 | 
            +
                        description=description,
         | 
| 10 | 
            +
                        expires_at=None
         | 
| 11 | 
            +
                    )
         | 
| @@ -0,0 +1,68 @@ | |
| 1 | 
            +
            from typing import Optional
         | 
| 2 | 
            +
            from urllib.parse import urljoin, urlencode
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            from hyperpocket.auth import AuthProvider
         | 
| 5 | 
            +
            from hyperpocket.auth.context import AuthContext
         | 
| 6 | 
            +
            from hyperpocket.auth.handler import AuthHandlerInterface, AuthenticateRequest
         | 
| 7 | 
            +
            from hyperpocket.auth.weaviate.token_context import WeaviateTokenAuthContext
         | 
| 8 | 
            +
            from hyperpocket.auth.weaviate.token_schema import WeaviateTokenResponse, WeaviateTokenRequest
         | 
| 9 | 
            +
            from hyperpocket.config import config
         | 
| 10 | 
            +
            from hyperpocket.futures import FutureStore
         | 
| 11 | 
            +
             | 
| 12 | 
            +
             | 
| 13 | 
            +
            class WeaviateTokenAuthHandler(AuthHandlerInterface):
         | 
| 14 | 
            +
                name: str = "weaviate-token"
         | 
| 15 | 
            +
                description: str = "This handler is used to authenticate users using the Weaviate token."
         | 
| 16 | 
            +
                scoped: bool = False
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                _TOKEN_URL: str = urljoin(config().public_base_url + "/", f"{config().callback_url_rewrite_prefix}/auth/token")
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                @staticmethod
         | 
| 21 | 
            +
                def provider() -> AuthProvider:
         | 
| 22 | 
            +
                    return AuthProvider.WEAVIATE
         | 
| 23 | 
            +
                
         | 
| 24 | 
            +
                @staticmethod
         | 
| 25 | 
            +
                def provider_default() -> bool:
         | 
| 26 | 
            +
                    return True
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                @staticmethod
         | 
| 29 | 
            +
                def recommended_scopes() -> set[str]:
         | 
| 30 | 
            +
                    return set()
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                def prepare(self, auth_req: WeaviateTokenRequest, thread_id: str, profile: str,
         | 
| 33 | 
            +
                            future_uid: str, *args, **kwargs) -> str:
         | 
| 34 | 
            +
                    redirect_uri = urljoin(
         | 
| 35 | 
            +
                        config().public_base_url + "/",
         | 
| 36 | 
            +
                        f"{config().callback_url_rewrite_prefix}/auth/weaviate/token/callback",
         | 
| 37 | 
            +
                    )
         | 
| 38 | 
            +
                    url = self._make_auth_url(auth_req=auth_req, redirect_uri=redirect_uri, state=future_uid)
         | 
| 39 | 
            +
                    FutureStore.create_future(future_uid, data={
         | 
| 40 | 
            +
                        "redirect_uri": redirect_uri,
         | 
| 41 | 
            +
                        "thread_id": thread_id,
         | 
| 42 | 
            +
                        "profile": profile,
         | 
| 43 | 
            +
                    })
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                    return f'User needs to authenticate using the following URL: {url}'
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                async def authenticate(self, auth_req: WeaviateTokenRequest, future_uid: str, *args, **kwargs) -> AuthContext:
         | 
| 48 | 
            +
                    future_data = FutureStore.get_future(future_uid)
         | 
| 49 | 
            +
                    access_token = await future_data.future
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                    response = WeaviateTokenResponse(access_token=access_token)
         | 
| 52 | 
            +
                    context = WeaviateTokenAuthContext.from_weaviate_token_response(response)
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                    return context
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                async def refresh(self, auth_req: WeaviateTokenRequest, context: AuthContext, *args, **kwargs) -> AuthContext:
         | 
| 57 | 
            +
                    raise Exception("Weaviate token doesn't support refresh")
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                def _make_auth_url(self, auth_req: WeaviateTokenRequest, redirect_uri: str, state: str):
         | 
| 60 | 
            +
                    params = {
         | 
| 61 | 
            +
                        "redirect_uri": redirect_uri,
         | 
| 62 | 
            +
                        "state": state,
         | 
| 63 | 
            +
                    }
         | 
| 64 | 
            +
                    auth_url = f"{self._TOKEN_URL}?{urlencode(params)}"
         | 
| 65 | 
            +
                    return auth_url
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                def make_request(self, auth_scopes: Optional[list[str]] = None, **kwargs) -> WeaviateTokenRequest:
         | 
| 68 | 
            +
                    return WeaviateTokenRequest()
         | 
| @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            from typing import List, Optional
         | 
| 2 | 
            +
            from pydantic import BaseModel
         | 
| 3 | 
            +
            from hyperpocket.auth.schema import AuthenticateRequest, AuthenticateResponse
         | 
| 4 | 
            +
            class WeaviateTokenRequest(AuthenticateRequest):
         | 
| 5 | 
            +
                pass
         | 
| 6 | 
            +
            class WeaviateTokenResponse(AuthenticateResponse):
         | 
| 7 | 
            +
                access_token: str
         | 
    
        hyperpocket/cli/eject.py
    CHANGED
    
    | @@ -10,10 +10,5 @@ import hyperpocket.repository as repository | |
| 10 10 | 
             
            @click.argument("url", type=str)
         | 
| 11 11 | 
             
            @click.argument("ref", type=str)
         | 
| 12 12 | 
             
            @click.argument("remote_path", type=str)
         | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
                if not lockfile:
         | 
| 16 | 
            -
                    lockfile = pathlib.Path.cwd() / "pocket.lock"
         | 
| 17 | 
            -
                if not lockfile.exists():
         | 
| 18 | 
            -
                    raise ValueError("To eject a tool, you first need to pull it")
         | 
| 19 | 
            -
                repository.eject(url, ref, remote_path, repository.Lockfile(lockfile))
         | 
| 13 | 
            +
            def eject(url: str, ref: str, remote_path: str):
         | 
| 14 | 
            +
                repository.eject(url, ref, remote_path)
         | 
    
        hyperpocket/cli/pull.py
    CHANGED
    
    | @@ -8,11 +8,6 @@ import hyperpocket.repository as repository | |
| 8 8 |  | 
| 9 9 | 
             
            @click.command()
         | 
| 10 10 | 
             
            @click.argument("url", type=str)
         | 
| 11 | 
            -
            @click.option("--lockfile", envvar="PATHS", type=click.Path(exists=True))
         | 
| 12 11 | 
             
            @click.option("--git-ref", type=str, default="HEAD")
         | 
| 13 | 
            -
            def pull(url: str,  | 
| 14 | 
            -
                 | 
| 15 | 
            -
                    lockfile = pathlib.Path.cwd() / "pocket.lock"
         | 
| 16 | 
            -
                if not lockfile.exists():
         | 
| 17 | 
            -
                    lockfile.touch()
         | 
| 18 | 
            -
                repository.pull(repository.Lockfile(path=lockfile), url, git_ref)
         | 
| 12 | 
            +
            def pull(url: str, git_ref: str):
         | 
| 13 | 
            +
                repository.pull(url, git_ref)
         | 
    
        hyperpocket/config/settings.py
    CHANGED
    
    | @@ -2,7 +2,7 @@ import os | |
| 2 2 | 
             
            from pathlib import Path
         | 
| 3 3 |  | 
| 4 4 | 
             
            from dynaconf import Dynaconf
         | 
| 5 | 
            -
            from pydantic import BaseModel, Field
         | 
| 5 | 
            +
            from pydantic import BaseModel, Field, Extra
         | 
| 6 6 |  | 
| 7 7 | 
             
            from hyperpocket.config.auth import AuthConfig, DefaultAuthConfig
         | 
| 8 8 | 
             
            from hyperpocket.config.session import DefaultSessionConfig, SessionConfig
         | 
| @@ -47,6 +47,7 @@ class Config(BaseModel): | |
| 47 47 | 
             
                auth: AuthConfig = DefaultAuthConfig
         | 
| 48 48 | 
             
                session: SessionConfig = DefaultSessionConfig
         | 
| 49 49 | 
             
                tool_vars: dict[str, str] = Field(default_factory=dict)
         | 
| 50 | 
            +
                docks: dict[str, dict] = Field(default_factory=dict)
         | 
| 50 51 |  | 
| 51 52 | 
             
                @property
         | 
| 52 53 | 
             
                def internal_base_url(self):
         | 
    
        hyperpocket/pocket_core.py
    CHANGED
    
    | @@ -1,53 +1,69 @@ | |
| 1 1 | 
             
            import asyncio
         | 
| 2 | 
            -
            import pathlib
         | 
| 3 2 | 
             
            from typing import Any, Callable, List, Optional, Union
         | 
| 4 3 |  | 
| 5 4 | 
             
            from hyperpocket.builtin import get_builtin_tools
         | 
| 6 5 | 
             
            from hyperpocket.config import pocket_logger
         | 
| 7 6 | 
             
            from hyperpocket.pocket_auth import PocketAuth
         | 
| 8 | 
            -
            from hyperpocket. | 
| 9 | 
            -
            from hyperpocket. | 
| 10 | 
            -
            from hyperpocket.tool import Tool, ToolRequest
         | 
| 7 | 
            +
            from hyperpocket.tool import Tool
         | 
| 8 | 
            +
            from hyperpocket.tool.dock import Dock
         | 
| 11 9 | 
             
            from hyperpocket.tool.function import from_func
         | 
| 12 | 
            -
            from hyperpocket.tool.wasm import WasmTool
         | 
| 13 | 
            -
            from hyperpocket.tool.wasm.tool import WasmToolRequest
         | 
| 14 10 | 
             
            from hyperpocket.tool_like import ToolLike
         | 
| 15 11 |  | 
| 16 12 |  | 
| 17 13 | 
             
            class PocketCore:
         | 
| 18 14 | 
             
                auth: PocketAuth
         | 
| 19 15 | 
             
                tools: dict[str, Tool]
         | 
| 20 | 
            -
                 | 
| 16 | 
            +
                docks: list[Dock]
         | 
| 21 17 |  | 
| 18 | 
            +
                @staticmethod
         | 
| 19 | 
            +
                def _default_dock() -> Dock:
         | 
| 20 | 
            +
                    try:
         | 
| 21 | 
            +
                        from hyperdock_container.dock import ContainerDock
         | 
| 22 | 
            +
                        pocket_logger.info("hyperdock-container is loaded.")
         | 
| 23 | 
            +
                        return ContainerDock()
         | 
| 24 | 
            +
                    except ImportError:
         | 
| 25 | 
            +
                        pocket_logger.warning("Failed to import hyperdock_container.")
         | 
| 26 | 
            +
                    
         | 
| 27 | 
            +
                    try:
         | 
| 28 | 
            +
                        from hyperdock_wasm.dock import WasmDock
         | 
| 29 | 
            +
                        pocket_logger.info("hyperdock-wasm is loaded.")
         | 
| 30 | 
            +
                        return WasmDock()
         | 
| 31 | 
            +
                    except ImportError:
         | 
| 32 | 
            +
                        raise ImportError("No default dock available. To register a remote tool, you need to install either hyperdock_wasm or hyperdock_container.")
         | 
| 33 | 
            +
                    
         | 
| 34 | 
            +
                
         | 
| 22 35 | 
             
                def __init__(
         | 
| 23 36 | 
             
                    self,
         | 
| 24 37 | 
             
                    tools: list[ToolLike],
         | 
| 25 38 | 
             
                    auth: PocketAuth = None,
         | 
| 26 | 
            -
                    lockfile_path: Optional[str] = None,
         | 
| 27 | 
            -
                    force_update: bool = False,
         | 
| 28 39 | 
             
                ):
         | 
| 29 40 | 
             
                    if auth is None:
         | 
| 30 41 | 
             
                        auth = PocketAuth()
         | 
| 31 42 | 
             
                    self.auth = auth
         | 
| 32 43 |  | 
| 33 | 
            -
                    #  | 
| 34 | 
            -
                    if  | 
| 35 | 
            -
             | 
| 36 | 
            -
                     | 
| 37 | 
            -
                    self. | 
| 38 | 
            -
             | 
| 39 | 
            -
                     | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 42 | 
            -
             | 
| 43 | 
            -
                         | 
| 44 | 
            -
                        self. | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 47 | 
            -
                    # load tool from tool_like
         | 
| 44 | 
            +
                    # filter strs out first and register the tools to default dock
         | 
| 45 | 
            +
                    str_tool_likes = [tool for tool in tools if (isinstance(tool, str) or isinstance(tool, tuple))]
         | 
| 46 | 
            +
                    function_tool_likes = [tool for tool in tools if (not isinstance(tool, str) and not isinstance(tool, Dock) and not isinstance(tool, tuple))]
         | 
| 47 | 
            +
                    # especially, docks are maintained by core
         | 
| 48 | 
            +
                    self.docks = [dock for dock in tools if isinstance(dock, Dock)]
         | 
| 49 | 
            +
                    
         | 
| 50 | 
            +
                    if len(str_tool_likes) > 0:
         | 
| 51 | 
            +
                        default_dock = self._default_dock()
         | 
| 52 | 
            +
                        for str_tool_like in str_tool_likes:
         | 
| 53 | 
            +
                            default_dock.plug(req_like=str_tool_like)
         | 
| 54 | 
            +
                        # append default dock
         | 
| 55 | 
            +
                        self.docks.append(default_dock)
         | 
| 56 | 
            +
             | 
| 48 57 | 
             
                    self.tools = dict()
         | 
| 49 | 
            -
                     | 
| 58 | 
            +
                    
         | 
| 59 | 
            +
                    # for each tool like, load the tool 
         | 
| 60 | 
            +
                    for tool_like in function_tool_likes:
         | 
| 50 61 | 
             
                        self._load_tool(tool_like)
         | 
| 62 | 
            +
                    
         | 
| 63 | 
            +
                    for dock in self.docks:
         | 
| 64 | 
            +
                        tools = dock.tools()
         | 
| 65 | 
            +
                        for tool in tools:
         | 
| 66 | 
            +
                            self._load_tool(tool)
         | 
| 51 67 |  | 
| 52 68 | 
             
                    pocket_logger.info(
         | 
| 53 69 | 
             
                        f"All Registered Tools Loaded successfully. total registered tools : {len(self.tools)}"
         | 
| @@ -253,8 +269,6 @@ class PocketCore: | |
| 253 269 | 
             
                    pocket_logger.info(f"Loading Tool {tool_like}")
         | 
| 254 270 | 
             
                    if isinstance(tool_like, Tool):
         | 
| 255 271 | 
             
                        tool = tool_like
         | 
| 256 | 
            -
                    elif isinstance(tool_like, ToolRequest):
         | 
| 257 | 
            -
                        tool = Tool.from_tool_request(tool_like, lockfile=self.lockfile)
         | 
| 258 272 | 
             
                    elif isinstance(tool_like, Callable):
         | 
| 259 273 | 
             
                        tool = from_func(tool_like)
         | 
| 260 274 | 
             
                    else:
         | 
| @@ -267,44 +281,3 @@ class PocketCore: | |
| 267 281 |  | 
| 268 282 | 
             
                    pocket_logger.info(f"Complete Loading Tool {tool.name}")
         | 
| 269 283 | 
             
                    return tool
         | 
| 270 | 
            -
             | 
| 271 | 
            -
                def _add_tool_like_lock_to_lockfile(self, tool_like: ToolLike):
         | 
| 272 | 
            -
                    if isinstance(tool_like, WasmToolRequest):  # lock is only in WasmToolRequest
         | 
| 273 | 
            -
                        self.lockfile.add_lock(tool_like.lock)
         | 
| 274 | 
            -
                    return
         | 
| 275 | 
            -
             | 
| 276 | 
            -
                @classmethod
         | 
| 277 | 
            -
                def _parse_tool_like(cls, tool_like: ToolLike) -> ToolLike:
         | 
| 278 | 
            -
                    if isinstance(tool_like, str):
         | 
| 279 | 
            -
                        return cls._parse_str_tool_like(tool_like)
         | 
| 280 | 
            -
             | 
| 281 | 
            -
                    elif isinstance(tool_like, WasmToolRequest):
         | 
| 282 | 
            -
                        return tool_like
         | 
| 283 | 
            -
             | 
| 284 | 
            -
                    elif isinstance(tool_like, ToolRequest):
         | 
| 285 | 
            -
                        raise ValueError(f"unreachable. tool_like:{tool_like}")
         | 
| 286 | 
            -
                    elif isinstance(tool_like, WasmTool):
         | 
| 287 | 
            -
                        raise ValueError("WasmTool should pass ToolRequest instance instead.")
         | 
| 288 | 
            -
                    else:  # Callable, Tool
         | 
| 289 | 
            -
                        return tool_like
         | 
| 290 | 
            -
             | 
| 291 | 
            -
                @classmethod
         | 
| 292 | 
            -
                def _parse_str_tool_like(cls, tool_like: str) -> ToolLike:
         | 
| 293 | 
            -
                    if pathlib.Path(tool_like).exists():
         | 
| 294 | 
            -
                        lock = LocalLock(tool_like)
         | 
| 295 | 
            -
                        parsed_tool_like = WasmToolRequest(lock=lock, rel_path="", tool_vars={})
         | 
| 296 | 
            -
                    elif tool_like.startswith("https://github.com"):
         | 
| 297 | 
            -
                        base_repo_url, git_ref, rel_path = GitLock.parse_repo_url(
         | 
| 298 | 
            -
                            repo_url=tool_like
         | 
| 299 | 
            -
                        )
         | 
| 300 | 
            -
                        lock = GitLock(repository_url=base_repo_url, git_ref=git_ref)
         | 
| 301 | 
            -
                        parsed_tool_like = WasmToolRequest(
         | 
| 302 | 
            -
                            lock=lock, rel_path=rel_path, tool_vars={}
         | 
| 303 | 
            -
                        )
         | 
| 304 | 
            -
                    else:
         | 
| 305 | 
            -
                        parsed_tool_like = None
         | 
| 306 | 
            -
                        RuntimeError(
         | 
| 307 | 
            -
                            f"Can't convert to ToolRequest. it might be wrong github url or local path. path: {tool_like}"
         | 
| 308 | 
            -
                        )
         | 
| 309 | 
            -
             | 
| 310 | 
            -
                    return parsed_tool_like
         | 
    
        hyperpocket/pocket_main.py
    CHANGED
    
    | @@ -1,5 +1,6 @@ | |
| 1 1 | 
             
            import asyncio
         | 
| 2 | 
            -
             | 
| 2 | 
            +
            import uuid
         | 
| 3 | 
            +
            from typing import Any, List, Union
         | 
| 3 4 |  | 
| 4 5 | 
             
            from hyperpocket.config import pocket_logger
         | 
| 5 6 | 
             
            from hyperpocket.pocket_auth import PocketAuth
         | 
| @@ -11,29 +12,39 @@ from hyperpocket.tool_like import ToolLike | |
| 11 12 | 
             
            class Pocket(object):
         | 
| 12 13 | 
             
                server: PocketServer
         | 
| 13 14 | 
             
                core: PocketCore
         | 
| 15 | 
            +
                _uid: str
         | 
| 14 16 |  | 
| 15 17 | 
             
                def __init__(
         | 
| 16 18 | 
             
                    self,
         | 
| 17 19 | 
             
                    tools: list[ToolLike],
         | 
| 18 20 | 
             
                    auth: PocketAuth = None,
         | 
| 19 | 
            -
                    lockfile_path: Optional[str] = None,
         | 
| 20 | 
            -
                    force_update: bool = False,
         | 
| 21 21 | 
             
                    use_profile: bool = False,
         | 
| 22 22 | 
             
                ):
         | 
| 23 | 
            -
                    self.use_profile = use_profile
         | 
| 24 | 
            -
                    self.core = PocketCore(
         | 
| 25 | 
            -
                        tools=tools,
         | 
| 26 | 
            -
                        auth=auth,
         | 
| 27 | 
            -
                        lockfile_path=lockfile_path,
         | 
| 28 | 
            -
                        force_update=force_update,
         | 
| 29 | 
            -
                    )
         | 
| 30 | 
            -
                    self.server = PocketServer()
         | 
| 31 | 
            -
                    self.server.run(self.core)
         | 
| 32 23 | 
             
                    try:
         | 
| 24 | 
            +
                        self._uid = str(uuid.uuid4())
         | 
| 25 | 
            +
                        self.use_profile = use_profile
         | 
| 26 | 
            +
                        self.server = PocketServer.get_instance_and_refcnt_up(self._uid)
         | 
| 33 27 | 
             
                        self.server.wait_initialized()
         | 
| 28 | 
            +
                        self.core = PocketCore(
         | 
| 29 | 
            +
                            tools=tools,
         | 
| 30 | 
            +
                            auth=auth,
         | 
| 31 | 
            +
                        )
         | 
| 34 32 | 
             
                    except Exception as e:
         | 
| 33 | 
            +
                        if hasattr(self, "server"):
         | 
| 34 | 
            +
                            self.server.refcnt_down(self._uid)
         | 
| 35 35 | 
             
                        pocket_logger.error(f"Failed to initialize pocket server. error : {e}")
         | 
| 36 36 | 
             
                        self._teardown_server()
         | 
| 37 | 
            +
                        raise e
         | 
| 38 | 
            +
                    
         | 
| 39 | 
            +
                    try:
         | 
| 40 | 
            +
                        asyncio.get_running_loop()
         | 
| 41 | 
            +
                    except RuntimeError:
         | 
| 42 | 
            +
                        loop = asyncio.new_event_loop()
         | 
| 43 | 
            +
                    else:
         | 
| 44 | 
            +
                        import nest_asyncio
         | 
| 45 | 
            +
                        loop = asyncio.new_event_loop()
         | 
| 46 | 
            +
                        nest_asyncio.apply(loop=loop)
         | 
| 47 | 
            +
                    loop.run_until_complete(self.server.plug_core(self._uid, self.core))
         | 
| 37 48 |  | 
| 38 49 | 
             
                def invoke(
         | 
| 39 50 | 
             
                    self,
         | 
| @@ -158,8 +169,17 @@ class Pocket(object): | |
| 158 169 | 
             
                    Returns:
         | 
| 159 170 | 
             
                        tuple[str, bool]: tool result and state.
         | 
| 160 171 | 
             
                    """
         | 
| 172 | 
            +
                    _kwargs = {
         | 
| 173 | 
            +
                        "tool_name": tool_name,
         | 
| 174 | 
            +
                        "body": body,
         | 
| 175 | 
            +
                        "thread_id": thread_id,
         | 
| 176 | 
            +
                        "profile": profile,
         | 
| 177 | 
            +
                        **kwargs,
         | 
| 178 | 
            +
                    }
         | 
| 179 | 
            +
             | 
| 161 180 | 
             
                    result, paused = await self.server.call_in_subprocess(
         | 
| 162 181 | 
             
                        PocketServerOperations.CALL,
         | 
| 182 | 
            +
                        self._uid,
         | 
| 163 183 | 
             
                        args,
         | 
| 164 184 | 
             
                        {
         | 
| 165 185 | 
             
                            "tool_name": tool_name,
         | 
| @@ -257,6 +277,7 @@ class Pocket(object): | |
| 257 277 | 
             
                ):
         | 
| 258 278 | 
             
                    prepare = await self.server.call_in_subprocess(
         | 
| 259 279 | 
             
                        PocketServerOperations.PREPARE_AUTH,
         | 
| 280 | 
            +
                        self._uid,
         | 
| 260 281 | 
             
                        args,
         | 
| 261 282 | 
             
                        {
         | 
| 262 283 | 
             
                            "tool_name": tool_name,
         | 
| @@ -278,6 +299,7 @@ class Pocket(object): | |
| 278 299 | 
             
                ):
         | 
| 279 300 | 
             
                    credentials = await self.server.call_in_subprocess(
         | 
| 280 301 | 
             
                        PocketServerOperations.AUTHENTICATE,
         | 
| 302 | 
            +
                        self._uid,
         | 
| 281 303 | 
             
                        args,
         | 
| 282 304 | 
             
                        {
         | 
| 283 305 | 
             
                            "tool_name": tool_name,
         | 
| @@ -300,6 +322,7 @@ class Pocket(object): | |
| 300 322 | 
             
                ):
         | 
| 301 323 | 
             
                    result = await self.server.call_in_subprocess(
         | 
| 302 324 | 
             
                        PocketServerOperations.TOOL_CALL,
         | 
| 325 | 
            +
                        self._uid,
         | 
| 303 326 | 
             
                        args,
         | 
| 304 327 | 
             
                        {
         | 
| 305 328 | 
             
                            "tool_name": tool_name,
         | 
| @@ -319,12 +342,10 @@ class Pocket(object): | |
| 319 342 | 
             
                    return self
         | 
| 320 343 |  | 
| 321 344 | 
             
                def __exit__(self, exc_type, exc_val, exc_tb):
         | 
| 322 | 
            -
                     | 
| 323 | 
            -
                        self.server.teardown()
         | 
| 345 | 
            +
                    self.server.refcnt_down(self._uid)
         | 
| 324 346 |  | 
| 325 347 | 
             
                def __del__(self):
         | 
| 326 | 
            -
                     | 
| 327 | 
            -
                        self.server.teardown()
         | 
| 348 | 
            +
                    self.server.refcnt_down(self._uid)
         | 
| 328 349 |  | 
| 329 350 | 
             
                def __getstate__(self):
         | 
| 330 351 | 
             
                    state = self.__dict__.copy()
         | 
| @@ -1,5 +1,4 @@ | |
| 1 | 
            -
            from hyperpocket.repository. | 
| 2 | 
            -
            from hyperpocket.repository. | 
| 3 | 
            -
            from hyperpocket.repository.repository import eject, pull, sync
         | 
| 1 | 
            +
            from hyperpocket.repository.tool_reference import ToolReference
         | 
| 2 | 
            +
            from hyperpocket.repository.repository import eject, pull
         | 
| 4 3 |  | 
| 5 | 
            -
            __all__ = [" | 
| 4 | 
            +
            __all__ = ["ToolReference", "pull", "eject"]
         | 
| @@ -1,43 +1,8 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            def pull(urllike: str, git_ref: str):
         | 
| 2 | 
            +
                # TODO
         | 
| 3 | 
            +
                pass
         | 
| 2 4 |  | 
| 3 | 
            -
            from hyperpocket.repository.lock import GitLock, LocalLock
         | 
| 4 | 
            -
            from hyperpocket.repository.lockfile import Lockfile
         | 
| 5 5 |  | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
                 | 
| 9 | 
            -
                if path.exists():
         | 
| 10 | 
            -
                    lockfile.add_lock(LocalLock(tool_path=str(path)))
         | 
| 11 | 
            -
                else:
         | 
| 12 | 
            -
                    lockfile.add_lock(GitLock(repository_url=urllike, git_ref=git_ref))
         | 
| 13 | 
            -
                lockfile.sync(force_update=False)
         | 
| 14 | 
            -
                lockfile.write()
         | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
            def sync(lockfile: Lockfile, force_update: bool):
         | 
| 18 | 
            -
                lockfile.sync(force_update=force_update)
         | 
| 19 | 
            -
                lockfile.write()
         | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
            def eject(url: str, ref: str, remote_path: str, lockfile: Lockfile):
         | 
| 23 | 
            -
                path = pathlib.Path(url)
         | 
| 24 | 
            -
                if path.exists():
         | 
| 25 | 
            -
                    raise ValueError("Local tools are already ejected")
         | 
| 26 | 
            -
                else:
         | 
| 27 | 
            -
                    lock = lockfile.get_lock(GitLock(repository_url=url, git_ref=ref).key())
         | 
| 28 | 
            -
             | 
| 29 | 
            -
                # first copy source to ejected directory
         | 
| 30 | 
            -
                working_dir = pathlib.Path.cwd()
         | 
| 31 | 
            -
                ejected_dir = working_dir / "ejected_tools"
         | 
| 32 | 
            -
                if not ejected_dir.exists():
         | 
| 33 | 
            -
                    ejected_dir.mkdir()
         | 
| 34 | 
            -
             | 
| 35 | 
            -
                tool_name = pathlib.Path(remote_path).name
         | 
| 36 | 
            -
                eject_path = ejected_dir / tool_name
         | 
| 37 | 
            -
                lock.eject_to_path(eject_path, remote_path)
         | 
| 38 | 
            -
             | 
| 39 | 
            -
                # then update lockfile
         | 
| 40 | 
            -
                lockfile.remove_lock(lock.key())
         | 
| 41 | 
            -
                lockfile.add_lock(LocalLock(tool_path=str(eject_path)))
         | 
| 42 | 
            -
                lockfile.sync(force_update=True)
         | 
| 43 | 
            -
                lockfile.write()
         | 
| 6 | 
            +
            def eject(url: str, ref: str, remote_path: str):
         | 
| 7 | 
            +
                # TODO
         | 
| 8 | 
            +
                pass
         | 
| @@ -0,0 +1,28 @@ | |
| 1 | 
            +
            import abc
         | 
| 2 | 
            +
            import pathlib
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            from pydantic import BaseModel
         | 
| 5 | 
            +
             | 
| 6 | 
            +
             | 
| 7 | 
            +
            class ToolReference(BaseModel, abc.ABC):
         | 
| 8 | 
            +
                tool_source: str = None
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                @abc.abstractmethod
         | 
| 11 | 
            +
                def __str__(self):
         | 
| 12 | 
            +
                    raise NotImplementedError
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                @abc.abstractmethod
         | 
| 15 | 
            +
                def key(self) -> str:
         | 
| 16 | 
            +
                    raise NotImplementedError
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                @abc.abstractmethod
         | 
| 19 | 
            +
                def sync(self, **kwargs):
         | 
| 20 | 
            +
                    raise NotImplementedError
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                def eject_to_path(self, dest_path: pathlib.Path, src_sub_path: str = None):
         | 
| 23 | 
            +
                    ## local locks are already tracked by git
         | 
| 24 | 
            +
                    raise NotImplementedError
         | 
| 25 | 
            +
             | 
| 26 | 
            +
             | 
| 27 | 
            +
             | 
| 28 | 
            +
             | 
| @@ -0,0 +1,27 @@ | |
| 1 | 
            +
            from fastapi import APIRouter
         | 
| 2 | 
            +
            from starlette.responses import HTMLResponse
         | 
| 3 | 
            +
            from hyperpocket.futures import FutureStore
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            weaviate_auth_router = APIRouter(
         | 
| 6 | 
            +
                prefix="/weaviate"
         | 
| 7 | 
            +
            )
         | 
| 8 | 
            +
             | 
| 9 | 
            +
             | 
| 10 | 
            +
            @weaviate_auth_router.get("/oauth2/callback")
         | 
| 11 | 
            +
            async def weaviate_oauth2_callback(state: str, code: str):
         | 
| 12 | 
            +
                try:
         | 
| 13 | 
            +
                    FutureStore.resolve_future(state, code)
         | 
| 14 | 
            +
                except ValueError:
         | 
| 15 | 
            +
                    return HTMLResponse(content="failed")
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                return HTMLResponse(content="success")
         | 
| 18 | 
            +
             | 
| 19 | 
            +
             | 
| 20 | 
            +
            @weaviate_auth_router.get("/token/callback")
         | 
| 21 | 
            +
            async def weaviate_token_callback(state: str, token: str):
         | 
| 22 | 
            +
                try:
         | 
| 23 | 
            +
                    FutureStore.resolve_future(state, token)
         | 
| 24 | 
            +
                except ValueError:
         | 
| 25 | 
            +
                    return HTMLResponse(content="failed")
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                return HTMLResponse(content="success")
         |