{"id":26577283,"url":"https://github.com/raiderrobert/amino","last_synced_at":"2025-03-23T03:37:06.633Z","repository":{"id":48911899,"uuid":"385244173","full_name":"raiderrobert/amino","owner":"raiderrobert","description":"A toolkit for custom rules engines","archived":false,"fork":false,"pushed_at":"2024-01-04T19:30:43.000Z","size":75,"stargazers_count":2,"open_issues_count":5,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-05-01T13:42:12.943Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/raiderrobert.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-07-12T12:43:13.000Z","updated_at":"2024-04-18T20:07:53.000Z","dependencies_parsed_at":"2023-01-18T01:31:10.368Z","dependency_job_id":null,"html_url":"https://github.com/raiderrobert/amino","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raiderrobert%2Famino","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raiderrobert%2Famino/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raiderrobert%2Famino/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raiderrobert%2Famino/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/raiderrobert","download_url":"https://codeload.github.com/raiderrobert/amino/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245052640,"owners_count":20553162,"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","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":[],"created_at":"2025-03-23T03:37:05.993Z","updated_at":"2025-03-23T03:37:06.596Z","avatar_url":"https://github.com/raiderrobert.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# amino\nA toolkit and DSL for custom rules engines. Much focus has been given toward AI and machine learning tooling to help take humans out of the loop. However, there are exist a wide variety of current and future applications for custom rules engines.\n\nWe propose inverting the problem space and placing a schema at the center, not unlike how GraphQL has done so for APIs.\n\nAmino has three parts:\n- a schema definition like graphql or protobuf for the data space it operates on\n- a pre-built small and extensible DSL for conditional logic to operate on these schemas\n- a runtime to evaluate the rules against the data set\n\n\n## How to Use\n\n\n### Basic Example\n\nDeclare a schema\n\nschema.amn\n```\namount: int\nstate_code: str\n```\n\nImport schema and evaluate a rule to see if it matches the matching variables passed in.\n\n```\n\u003e\u003e\u003e import amino\n\u003e\u003e\u003e amn = amino.load_schema(\"schema.amn\")\n\u003e\u003e\u003e amn.eval(\"amount \u003e 0 and state_code = 'CA'\", {\"amount\": 100, \"state_code\": \"CA\"})\nTrue\n\u003e\u003e\u003e amn.eval(\"amount \u003e 0 and state_code = 'CA'\", {\"amount\": 0, \"state_code\": \"CA\"})\nFalse\n```\n\n\n### More likely Runtime Example\n\nDeclare a schema\n\nschema.amn\n```\namount: int\nstate_code: str\n```\n\nImport schema and use it in your code. ( Note: You don't need to specify `id` for just one data set or one rule, but\nyou do need an `id` for more than one of either, and each `id` must unique. )\n\n```\n\u003e\u003e\u003e import amino\n\u003e\u003e\u003e amn = amino.load_schema(\"schema.amn\")\n\u003e\u003e\u003e compiled = amn.compile(\n...    [\n...        {\"id\": 1, \"rule\":\"amount \u003e 0 and state_code = 'CA'\"},\n...        {\"id\": 2, \"rule\":\"amount \u003e 10 and state_code = 'CA'\"},\n...        {\"id\": 3, \"rule\":\"amount \u003e= 100\"},\n...    ]\n... )\n\u003e\u003e\u003e compiled.eval([\n...    {\"id\": 45, \"amount\": 100, \"state_code\": \"CA\"},\n...    {\"id\": 46, \"amount\": 50, \"state_code\": \"CA\"},\n...    {\"id\": 47, \"amount\": 100, \"state_code\": \"NY\"},\n...    {\"id\": 48, \"amount\": 10, \"state_code\": \"NY\"},\n... ])\n\n[\n    {\"id\": 45, \"results\": [1, 2, 3]}, \n    {\"id\": 46, \"results\": [1, 2]},\n    {\"id\": 47, \"results\": [3]},\n    {\"id\": 48, \"results\": []},\n]\n```\n\nWe also support returning just one match.\n\n```\n\u003e\u003e\u003e import amino\n\u003e\u003e\u003e amn = amino.load_schema(\"schema.amn\")\n\u003e\u003e\u003e compiled = amn.compile(\n...    [\n...        {\"id\": 1, \"rule\":\"amount \u003e 0 and state_code = 'CA'\", \"ordering\": 3},\n...        {\"id\": 2, \"rule\":\"amount \u003e 10 and state_code = 'CA'\", \"ordering\": 2},\n...        {\"id\": 3, \"rule\":\"amount \u003e= 100\", \"ordering\": 1},\n...    ],\n...    match={\"option\": \"first\", \"key\": \"ordering\", \"ordering\": \"asc\"}\n... )\n\u003e\u003e\u003e compiled.eval([\n...    {\"id\": 100, \"amount\": 100, \"state_code\": \"CA\"},\n...    {\"id\": 101, \"amount\": 50, \"state_code\": \"CA\"},\n...    {\"id\": 102, \"amount\": 50, \"state_code\": \"NY\"},\n... ])\n\n[\n    {\"id\": 100, \"results\": [3]}, \n    {\"id\": 101, \"results\": [2]},\n    {\"id\": 102, \"results\": []}\n]\n```\n\n\n## Schema Elaboration\n\n\n### Comments\nWe support comments with the `#` symbol. Anything to the right of the comment symbol is disregarded at runtime.\n\nschema.amn\n```\n# this is a comment\namount: int  # this is too\nstate_code: str\n```\n\n\n### Structs\nWe support C-like structs with the `struct` keyword\n\nschema.amn\n```\nstruct applicant {\n    state_code: str,\n}\n\nstruct loan {\n    amount: int\n}\n\n```\n\n\n```\n\u003e\u003e\u003e data = {\"loan\": {\"amount\": 100}, \"applicant\": \"state_code\": \"CA\"\n\u003e\u003e\u003e rule = \"loan.amount \u003e 0 and applicant.state_code = 'CA'\"\n\u003e\u003e\u003e amn.eval(rule, data)\nTrue\n```\n\n\n### Functions\nWe support function declarations; you declare the inputs and output, and you \nimplement the function in your own language. These aren't true functions. It may be more appropriate to call it a \nforeign function interface declaration. That is, amino is the host language, and your implementation language in your\nproject (e.g. Python, TypeScript, etc.) is the guest language.\n\n\n\nschema.amn\n```\namount: int\nstate_code: str\n\nsmallest_number: (int, int) -\u003e int\n\n```\n\nNote the passing of `min` and passing it into the `funcs` argument while loading the schema. This provides the DSL host language access to calling out to the guest function `min` while the host function in the DSL uses `smallest_number`.\n\n```\n\u003e\u003e\u003e amn = amino.load_schema(\"schema.amn\", funcs={'smallest_number': min})\n\u003e\u003e\u003e data = {\"amount\": 100, \"state_code\": \"CA\"}\n\u003e\u003e\u003e rule = \"smallest_number(amount, 1000) \u003c 1000 and state_code = 'CA'\"\n\u003e\u003e\u003e amn.eval(rule, data)\nTrue\n```\n\n#### Default Arguments\n\nFunctions also support more complex cases, such as referencing other variables in the schema:\n\nschema.amn\n```\nCOMPANY_MAX_LOAN_AMT: int = 100_000\n\nloan_amount: int\napproved_amount: int\nstate_code: str\n\nwithin_tolerances: (COMPANY_MAX_LOAN_AMT)(loan_amount, approved_amount) -\u003e bool\n\n```\n\nNote the passing of `within_tolerances` and passing it into the `funcs` argument while loading the schema.\n\nAt runtime, three variables in the order provided will be passed to `within_tolerances`\n\n```\n\u003e\u003e\u003e import custom_module\n\u003e\u003e\u003e amn = amino.load_schema(\"schema.amn\", {'within_tolerances': custom_module.within_tolerances})\n\u003e\u003e\u003e data = {\"amount\": 100, \"state_code\": \"CA\"}\n\u003e\u003e\u003e rule = \"within_tolerances(10_000, 90_000) and state_code = 'CA'\"\n\u003e\u003e\u003e amn.eval(rule, data)\nTrue\n```\n\n\n\n### Lists\n\nWe support homogenous or heterogeneous arrays with the `list` keyword\n\nschema.amn\n```\nstate_code: str\namounts: list[int]\nthings: list[int|str|float]\n```\n\n\n\n```\n\u003e\u003e\u003e data = {\"amount\": 100, \"state_code\": \"CA\", \"things\": [\"CA\", 1, 1.0] }\n\u003e\u003e\u003e rule = \"amount \u003e 0 and state_code = 'CA' or state_code in things\"\n\u003e\u003e\u003e amn.eval(rule, data)\nTrue\n```\n\n## Operators\n\nBuilt-in operators.\n\n```\n!=\n=\n\u003e\n\u003c\n\u003e=\n\u003c=\nin\nnot in\nnot\nand\nor\n\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fraiderrobert%2Famino","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fraiderrobert%2Famino","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fraiderrobert%2Famino/lists"}