{"id":30896901,"url":"https://github.com/circlefin/circle-ooak","last_synced_at":"2026-02-26T18:32:46.662Z","repository":{"id":313819558,"uuid":"1003780786","full_name":"circlefin/circle-ooak","owner":"circlefin","description":"OOAK: Object-Oriented Agent Kit.","archived":false,"fork":false,"pushed_at":"2025-09-08T16:53:38.000Z","size":39,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-09-08T18:13:09.532Z","etag":null,"topics":["agents","ai","intents","research","workflow"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/circlefin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.Apache","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-06-17T16:51:49.000Z","updated_at":"2025-09-08T16:53:21.000Z","dependencies_parsed_at":"2025-09-08T18:13:12.745Z","dependency_job_id":"1b21ae55-82df-4cd6-8c64-450a9d6beeca","html_url":"https://github.com/circlefin/circle-ooak","commit_stats":null,"previous_names":["circlefin/circle-ooak"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/circlefin/circle-ooak","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/circlefin%2Fcircle-ooak","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/circlefin%2Fcircle-ooak/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/circlefin%2Fcircle-ooak/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/circlefin%2Fcircle-ooak/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/circlefin","download_url":"https://codeload.github.com/circlefin/circle-ooak/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/circlefin%2Fcircle-ooak/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274231140,"owners_count":25245675,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-08T02:00:09.813Z","response_time":121,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agents","ai","intents","research","workflow"],"created_at":"2025-09-08T23:47:43.127Z","updated_at":"2026-02-26T18:32:41.624Z","avatar_url":"https://github.com/circlefin.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Circle OOAK: Object-Oriented Agent Kit\n\n## License\nThis work is licensed under Apache 2.0. See SPDX-License-Identifier in the file headings.\n\n`SPDX-License-Identifier: Apache-2.0`\n\nIt has not been audited, comes with\nno guarantees, and is provided as is. Use at your own risk.\n\n## Introduction\nThis project creates an extension to the OpenAI Agents SDK.\n\n- The `@agent_tool` decorator can be used with object instance methods instead of `@function_tool` which only\nsupports static functions. \n- The `@secure_tool` decorator  can be used instead of the `@agent_tool` decorator\nto add before/after hooks to your tool code. \n- An `InstanceAgent` that can use `@agent_tool` and `@secure_tool`. An `InstanceAgent` is a subclass of\na regular OpenAI Agents SDK `Agent` and can interact with other agents via handoffs and guardrails.\n\n\nThis package includes a `WorkflowManager` that implements the abstract `SecurityContext`,\nthat checks that intended actions have been approved.\n\n1. Create intent. The agent calls the @secure_tool function with `wfid=None` argument. Instead of\nexecuting the function, it returns an intent: a json representation of the function call.\n2. Get Approval. The agent calls the WorkflowManager with a list of intents. The WorkflowManager\napproves the list of intents and returns a WorkflowId.\n3. Execute. The agent now calls the @secure_tools in the correct order with the WorkflowId. The\nWorkflowManager ensures that each subsequent function call matches the approved workflow.\n\nSample code can be found at `https://github.com/circlefin/circle-ooak/example`\n\nBelow is an exmple of a `WalletWorkflowAgent`.\n\n```python\nfrom circle_ooak.instance_agent import InstanceAgent\nfrom circle_ooak.secure_tool import secure_tool\nfrom circle_ooak.workflow_manager import WorkflowManager\n\nclass WalletWorkflowAgent(InstanceAgent):\n    instructions = \"\"\"\n    You help users execute Ethereum transactions. Do the following steps to help the user:\n    1. Create a workflow of intents by calling each secure tool with wfid=None to get the intents\n    2. Call approve_workflow with the list of intents to get a workflow id \n    3. Execute the workflow by calling each secure tool again. You MUST include the wfid parameter with the workflow id you got in step 2.\n    4. Print the final tx hash for every transaction.\n    \n    You do not need approval from the user to execute a workflow if you have the workflow id.\n    \"\"\"\n    def __init__(self, name: str, model: OpenAIChatCompletionsModel, wallets: dict[str, Wallet]):\n        self.wallets = wallets\n        tools = [self.approve_workflow]\n        agent_tools = [self.send_usdc, self.mint_usdc]\n        super().__init__(name=name, instructions=self.instructions, model=model, tools=tools, agent_tools=agent_tools)\n\n    @function_tool\n    def approve_workflow(ctxt: RunContextWrapper[WorkflowManager], workflow: list[str]):\n        \"\"\"Approve a workflow of secure tool calls.\n        workflow: a list of intents (json strings).\n        returns: a string with the workflow id\n        \"\"\"\n        manager = ctxt.context\n        response = manager.approve(workflow)\n        if response.approved:\n            return f\"Workflow approved: with wfid {response.msg}\"\n        else:\n            return f\"Workflow not approved: {response.msg}\"\n\n    @secure_tool\n    def send_usdc(self, ctxt: RunContextWrapper[WorkflowManager], wfid: str, sender: str, receiver: str, amount: int):\n        wallet = self.wallets[sender]\n        if wallet is None:\n            return f\"Wallet {sender} not found\"\n        return wallet.send_usdc(receiver, amount)\n\n# Sample agent\nwallets = {\n    \"0x111111\": Wallet(\"0x111111\"),\n    \"0x222222\": Wallet(\"0x222222\"),\n    \"0x333333\": Wallet(\"0x333333\"),\n}\nagent = WalletWorkflowAgent(\n    name=\"Secure Agent\",\n    model=model,\n    wallets=wallets\n)\n```\n\n\n## Setup Python environment\nInstall the `circle-ooak` package and other dependencies:\n\n```shell\npip install circle-ooak\npip install dotenv openai openai-agents\n```\n\nAlternatively, you can clone the GitHub Repo and install using \nthe `requirements.txt` file:\n```shell\ngit clone http://github.com/circlefin/circle-ooak\ncd circle-ooak\npip install -r requirements.txt\npip install circle-ooak\n```\n\nWe recommend you use a virtual environment:\n```shell\n# create an environment\npython -m venv .venv\n\n# activate the environment\nsource .venv/bin/activate\n\n# de-activate the environment\ndeactivate\n```\n\n## Setup Environment\nCreate an `.env` file. You must obtain an OpenAI API key.\n\n```shell\n# External: get API key from https://platform.openai.com/api-keys\nOPENAI_API_KEY=api_key_goes_here\n\n# URL to connect to OpenAI\nOPENAI_URL=https://api.openai.com/v1/models\n\n# OpenAI model to use\nOPENAI_MODEL=gpt-4o\n```\n\n## Run demo\nYou must setup your LLM using the `.env` file to run the demo.\n\n```shell\n# Download demo\ngit clone http://github.com/circlefin/circle-ooak\ncd circle-ooak\npip install -r requirements.txt\n\n# To run a Wallet Workflow Agent \npython example/run_agent.py\n\n# To run a Wallet Instance Agent \npython example/run_agent.py instance\n\n# To run unit tests\npython -m pytest test/model_unit_test.py -v\n```\n\nHere is sample output from one run:\n\n```shell\n  Ask a question or type 'exit': Have 0x111111 mint 10 USDC to 0x222222 and then have 0x222222 send 5 USDC to 0x333333.\n\nLOG: Approving workflow with intents: ['{\"function\": \"mint_usdc\", \"arguments\": {\"minter\": \"0x111111\", \"receiver\": \"0x222222\", \"amount\": 10}, \"instance\": \"Secure Agent\"}', '{\"function\": \"send_usdc\", \"arguments\": {\"sender\": \"0x222222\", \"receiver\": \"0x333333\", \"amount\": 5}, \"instance\": \"Secure Agent\"}']\nLOG: Approved workflow f0082344-018c-4d5c-856a-fb8989ab6bf2 with intents: [{\"function\": \"mint_usdc\", \"arguments\": {\"minter\": \"0x111111\", \"receiver\": \"0x222222\", \"amount\": 10}, \"instance\": \"Secure Agent\"}, {\"function\": \"send_usdc\", \"arguments\": {\"sender\": \"0x222222\", \"receiver\": \"0x333333\", \"amount\": 5}, \"instance\": \"Secure Agent\"}].\nOverride this method with your own approval logic.\n\nLOG: Starting action {\"function\": \"mint_usdc\", \"arguments\": {\"minter\": \"0x111111\", \"receiver\": \"0x222222\", \"amount\": 10}, \"instance\": \"Secure Agent\"}\nMinting 10 USDC by 0x111111 to 0x222222\nLOG: Finished action {\"function\": \"mint_usdc\", \"arguments\": {\"minter\": \"0x111111\", \"receiver\": \"0x222222\", \"amount\": 10}, \"instance\": \"Secure Agent\"} with result txhash=0987654321\n\nLOG: Starting action {\"function\": \"send_usdc\", \"arguments\": {\"sender\": \"0x222222\", \"receiver\": \"0x333333\", \"amount\": 5}, \"instance\": \"Secure Agent\"}\nSending 5 USDC from 0x222222 to 0x333333\nLOG: Finished action {\"function\": \"send_usdc\", \"arguments\": {\"sender\": \"0x222222\", \"receiver\": \"0x333333\", \"amount\": 5}, \"instance\": \"Secure Agent\"} with result txhash=1234567890\n\nLOG: Workflow completed successfully with result txhash=1234567890\nThe transactions were successfully executed. Here are the transaction hashes:\n\n1. Mint 10 USDC from `0x111111` to `0x222222`: `txhash=0987654321`\n2. Send 5 USDC from `0x222222` to `0x333333`: `txhash=1234567890`\n\n```\n\n## Dev notes\nFunctions decorated with `@secure_tool` must include the following two arguments:\n- `wfid: WorkflowId`. Agents will use the workflow id to get permission to perform tasks.\n- `ctxt: RunContextWrapper[SecurityContext]`. The runner must provide an object as context that\nimplements the abstract class `SecurityContext` such as the `WorkflowManager` included in this project.\n\n\nAgent tools with the `@secure_tool` or `@agent_tool` decorators can be tested the same way as those with `@function_tool`.\nWe include a model unit test file.\n\n```shell\npython -m pytest test/model_unit_test.py -v\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcirclefin%2Fcircle-ooak","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcirclefin%2Fcircle-ooak","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcirclefin%2Fcircle-ooak/lists"}