From 27c99c0175e5abb3d404814cd223166bcbc2473a Mon Sep 17 00:00:00 2001 From: Stefan Krawczyk Date: Mon, 1 Apr 2024 23:56:52 -0700 Subject: [PATCH 1/5] Translating to burr Step (1) big actions. Step (2) write smaller actions that mirror the flow chart. --- .idea/.gitignore | 8 + .idea/Jaiqu.iml | 12 ++ .idea/inspectionProfiles/Project_Default.xml | 15 ++ .../inspectionProfiles/profiles_settings.xml | 6 + .idea/misc.xml | 7 + .idea/modules.xml | 8 + .idea/vcs.xml | 6 + jaiqu/__init__.py | 2 +- jaiqu/helpers.py | 2 +- jaiqu/jaiqu | 15 ++ jaiqu/jaiqu.png | Bin 0 -> 31520 bytes jaiqu/jaiqu.py | 18 +- jaiqu/jaiqu_burr.py | 156 ++++++++++++++++++ requirements.txt | 3 +- 14 files changed, 248 insertions(+), 10 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/Jaiqu.iml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 jaiqu/jaiqu create mode 100644 jaiqu/jaiqu.png create mode 100644 jaiqu/jaiqu_burr.py diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/Jaiqu.iml b/.idea/Jaiqu.iml new file mode 100644 index 0000000..8b8c395 --- /dev/null +++ b/.idea/Jaiqu.iml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..d5fa4af --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,15 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..d3511d7 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..bee6e19 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/jaiqu/__init__.py b/jaiqu/__init__.py index 5741c0e..d28ffc6 100644 --- a/jaiqu/__init__.py +++ b/jaiqu/__init__.py @@ -1 +1 @@ -from .jaiqu import validate_schema, translate_schema +# from .jaiqu import validate_schema, translate_schema diff --git a/jaiqu/helpers.py b/jaiqu/helpers.py index a577283..f3de5d9 100644 --- a/jaiqu/helpers.py +++ b/jaiqu/helpers.py @@ -67,7 +67,7 @@ def identify_key(key, value, input_schema, openai_api_key=None, key_hints=None) return to_key(completion), completion -def create_jq_string(input_schema, key, value, openai_api_key) -> str: +def create_jq_string(input_schema, key, value, openai_api_key=None) -> str: messages: list[ChatCompletionMessageParam] = [{ "role": "system", "content": f"""You are a perfect jq engineer designed to validate and extract data from JSON files using jq. Only reply with code. Do NOT use any natural language. Do NOT use markdown i.e. ```. diff --git a/jaiqu/jaiqu b/jaiqu/jaiqu new file mode 100644 index 0000000..5abaa7f --- /dev/null +++ b/jaiqu/jaiqu @@ -0,0 +1,15 @@ +digraph { + graph [compound=false concentrate=false rankdir=TB ranksep=0.4] + validate_schema [label=validate_schema shape=box style=rounded] + create_jq_filter_query [label=create_jq_filter_query shape=box style=rounded] + validate_json [label=validate_json shape=box style=rounded] + error_state [label=error_state shape=box style=rounded] + good_result [label=good_result shape=box style=rounded] + validate_schema -> create_jq_filter_query [style=solid] + create_jq_filter_query -> validate_json [label="'exit' in question" style=dashed] + validate_json -> good_result [label=valid_json style=dashed] + validate_schema -> good_result [label=valid_schema style=dashed] + validate_schema -> error_state [label="not valid_schema" style=dashed] + create_jq_filter_query -> error_state [label=max_retries_hit style=dashed] + validate_json -> error_state [label=max_retries_hit style=dashed] +} diff --git a/jaiqu/jaiqu.png b/jaiqu/jaiqu.png new file mode 100644 index 0000000000000000000000000000000000000000..655ce66b8aaf148a668a67aa59181b65ab158d5b GIT binary patch literal 31520 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 V(a(J@a?!M4X>Mb7(bUQRe*r*5m+t@o literal 0 HcmV?d00001 diff --git a/jaiqu/jaiqu.py b/jaiqu/jaiqu.py index abe8841..1d7f591 100644 --- a/jaiqu/jaiqu.py +++ b/jaiqu/jaiqu.py @@ -1,12 +1,12 @@ - import jq import json from jsonschema import validate from tqdm.auto import tqdm # Use the auto submodule for notebook-friendly output if necessary -from .helpers import identify_key, create_jq_string, repair_query, dict_to_jq_filter +from helpers import identify_key, create_jq_string, repair_query, dict_to_jq_filter -def validate_schema(input_json: dict, output_schema: dict, openai_api_key: str | None = None, key_hints=None) -> tuple[dict, bool]: +def validate_schema(input_json: dict, output_schema: dict, openai_api_key: str | None = None, key_hints=None) -> tuple[ + dict, bool]: """Validates the schema of the input JSON against the output schema. Args: input_json (dict): The input JSON parsed into a dictionary. @@ -44,7 +44,8 @@ def validate_schema(input_json: dict, output_schema: dict, openai_api_key: str | return results, valid -def translate_schema(input_json, output_schema, openai_api_key: str | None = None, key_hints=None, max_retries=10) -> str: +def translate_schema(input_json, output_schema, openai_api_key: str | None = None, key_hints=None, + max_retries=10) -> str: """ Translate the input JSON schema into a filtering query using jq. @@ -64,7 +65,8 @@ def translate_schema(input_json, output_schema, openai_api_key: str | None = Non RuntimeError: If failed to validate the jq filter after maximum retries. """ - schema_properties, is_valid = validate_schema(input_json, output_schema, key_hints=key_hints, openai_api_key=openai_api_key) + schema_properties, is_valid = validate_schema(input_json, output_schema, key_hints=key_hints, + openai_api_key=openai_api_key) if not is_valid: raise RuntimeError( f"The input JSON does not contain the required data to satisfy the output schema: \n\n{json.dumps(schema_properties, indent=2)}") @@ -73,7 +75,9 @@ def translate_schema(input_json, output_schema, openai_api_key: str | None = Non filter_query = {} - with tqdm(total=len(filtered_schema), desc="Translating schema") as pbar, tqdm(total=max_retries, desc="Retry attempts") as pbar_retries: + with tqdm(total=len(filtered_schema), + desc="Translating schema") as pbar, tqdm(total=max_retries, + desc="Retry attempts") as pbar_retries: for key, value in filtered_schema.items(): pbar.set_postfix_str(f"Key: {key}", refresh=True) jq_string = create_jq_string(input_json, key, value, openai_api_key) @@ -114,5 +118,5 @@ def translate_schema(input_json, output_schema, openai_api_key: str | None = Non if tries >= max_retries: raise RuntimeError(f"Failed to validate the jq filter after {max_retries} retries.") complete_filter = repair_query(complete_filter, str(e), input_json, openai_api_key) - pbar.close() + pbar_validation.close() return complete_filter diff --git a/jaiqu/jaiqu_burr.py b/jaiqu/jaiqu_burr.py new file mode 100644 index 0000000..43c16d7 --- /dev/null +++ b/jaiqu/jaiqu_burr.py @@ -0,0 +1,156 @@ +import pprint +from typing import List, Optional, Tuple + +import burr.core +from burr.core import Action, Application, ApplicationBuilder, State, default, expr +from burr.core.action import action +from burr.lifecycle import LifecycleAdapter, PostRunStepHook, PreRunStepHook + +import jq +import json +from jsonschema import validate +from tqdm.auto import tqdm # Use the auto submodule for notebook-friendly output if necessary +from helpers import identify_key, create_jq_string, repair_query, dict_to_jq_filter + +@action( + reads=["input_json", "output_schema", "key_hints"], + writes=["valid_schema", "schema_properties"] +) +def validate_schema(state: State) -> tuple[dict, State]: + output_schema = state["output_schema"] + input_json = state["input_json"] + key_hints = state["key_hints"] + results = {} + valid = True + with tqdm(total=len(output_schema['properties']), desc="Validating schema") as pbar: + for key, value in output_schema['properties'].items(): + pbar.set_postfix_str(f"Key: {key}", refresh=True) + response_key, response_reasoning = identify_key(key, value, input_json, None, key_hints) + + if response_key is not None: + results[key] = {"identified": True, "key": response_key, + "message": response_reasoning, + **value} + else: + results[key] = {"identified": False, "key": response_key, + "message": response_reasoning, + **value} + if key in output_schema['required']: + results[key]['required'] = True + if results[key]['identified'] == False: + valid = False + else: + results[key]['required'] = False + pbar.update(1) + + state = state.update(valid_schema=valid, schema_properties=results) + return results, state + +@action( + reads=["input_json", "schema_properties", "max_retries"], + writes=["max_retries_hit", "jq_filter"] +) +def create_jq_filter_query(state: State) -> tuple[dict, State]: + schema_properties = state["schema_properties"] + input_json = state["input_json"] + max_retries = state["max_retries"] + filtered_schema = {k: v for k, v in schema_properties.items() if v['identified'] == True} + + filter_query = {} + + with tqdm(total=len(filtered_schema), + desc="Translating schema") as pbar, tqdm(total=max_retries, + desc="Retry attempts") as pbar_retries: + for key, value in filtered_schema.items(): + pbar.set_postfix_str(f"Key: {key}", refresh=True) + jq_string = create_jq_string(input_json, key, value) + + if jq_string == "None": # If the response is empty, skip the key + pbar.update(1) + continue + + tries = 0 + while True: + try: + jq.compile(jq_string).input(input_json).all() + break + except Exception as e: + tries += 1 + pbar_retries.update(1) + jq_string = repair_query(jq_string, str(e), input_json, None) + if tries >= max_retries: + state = state.update(max_retries_hit=True, jq_filter=None) + return {"error": f"Failed to create a valid jq filter for key '{key}' after {max_retries} retries."}, state + pbar.update(1) + filter_query[key] = jq_string + pbar.close() + pbar_retries.close() + complete_filter = dict_to_jq_filter(filter_query) + state = state.update(jq_filter=complete_filter, max_retries_hit=False) + return {"filter": complete_filter}, state + +@action( + reads=["input_json", "jq_filter", "output_schema", "max_retries"], + writes=["max_retries_hit", "valid_json", "complete_filter"] +) +def validate_json(state: State) -> tuple[dict, State]: + output_schema = state["output_schema"] + complete_filter = state["jq_filter"] + input_json = state["input_json"] + max_retries = state["max_retries"] + # Validate JSON + tries = 0 + with tqdm(total=max_retries, desc="Validation attempts") as pbar_validation: + while True: + try: + result = jq.compile(complete_filter).input(input_json).all()[0] + validate(instance=result, schema=output_schema) + pbar_validation.close() + break + except Exception as e: + tries += 1 + pbar_validation.update(1) + if tries >= max_retries: + state = state.update(max_retries_hit=True, valid_json=False, complete_filter=complete_filter) + return { + "error": f"Failed to validate the jq filter after {max_retries} retries."}, state + + complete_filter = repair_query(complete_filter, str(e), input_json, None) + state = state.update(complete_filter=complete_filter, max_retries_hit=False, valid_json=True) + return {"complete_filter": complete_filter}, state + + +if __name__ == '__main__': + app = ( + ApplicationBuilder() + .with_state( + **{ + "input_json": "", + "output_schema": "", + } + ) + .with_actions( + # bind the vector store to the AI conversational step + validate_schema=validate_schema, + create_jq_filter_query=create_jq_filter_query, + validate_json=validate_json, + error_state=burr.core.Result("chat_history"), + good_result=burr.core.Result("chat_history"), + ) + .with_transitions( + ("validate_schema", "create_jq_filter_query", default), + ("create_jq_filter_query", "validate_json", expr("'exit' in question")), + ("validate_json", "good_result", expr("valid_json")), + ("validate_schema", "good_result", expr("valid_schema")), + ("validate_schema", "error_state", expr("not valid_schema")), + ("create_jq_filter_query", "error_state", expr("max_retries_hit")), + ("validate_json", "error_state", expr("max_retries_hit")), + ) + .with_entrypoint("validate_schema") + .with_tracker(project="example:jaiqu") + .with_identifiers(partition_key="dagworks") + .build() + ) + app.visualize( + output_file_path="jaiqu", include_conditions=True, view=True, format="png" + ) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index dbdce18..cbe6978 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ jq==1.6.0 openai>=1.12.0,<2.0.0 -jsonschema==4.21.1 \ No newline at end of file +jsonschema==4.21.1 +burr \ No newline at end of file From 88760916174054153edbee29a63b625be7f6eb63 Mon Sep 17 00:00:00 2001 From: Stefan Krawczyk Date: Wed, 3 Apr 2024 22:40:22 -0700 Subject: [PATCH 2/5] Adds two implementations of Jaiqu using Burr jaiqu_burr_straight_port takes the code and maps it directly to burr. jaiqu_burr_granular takes the straight port and breaks it up into more actions. Note: I was hoping to use the mermaid diagram and do something reflecting that but the logic in the mermaid diagram doesn't really map to the code and thus isn't a good thing to model -- but the more granualr version does kind of look like it. --- jaiqu/jaiqu_burr_granular.py | 237 ++++++++++++++++++ ...qu_burr.py => jaiqu_burr_straight_port.py} | 97 +++++-- jaiqu/jaiqu_granular.png | Bin 0 -> 46778 bytes jaiqu/jaiqu_port.png | Bin 0 -> 32833 bytes 4 files changed, 313 insertions(+), 21 deletions(-) create mode 100644 jaiqu/jaiqu_burr_granular.py rename jaiqu/{jaiqu_burr.py => jaiqu_burr_straight_port.py} (64%) create mode 100644 jaiqu/jaiqu_granular.png create mode 100644 jaiqu/jaiqu_port.png diff --git a/jaiqu/jaiqu_burr_granular.py b/jaiqu/jaiqu_burr_granular.py new file mode 100644 index 0000000..c78991e --- /dev/null +++ b/jaiqu/jaiqu_burr_granular.py @@ -0,0 +1,237 @@ +import burr.core +from burr.core import ApplicationBuilder, State, default, when +from burr.core.action import action + +import jq +from jsonschema import validate +from helpers import identify_key, create_jq_string, repair_query, dict_to_jq_filter + + +@action( + reads=["input_json", "output_schema", "key_hints", "schema_properties"], + writes=["valid_schema", "schema_properties", "schema_processed"] +) +def validate_schema(state: State) -> tuple[dict, State]: + output_schema = state["output_schema"] + input_json = state["input_json"] + key_hints = state["key_hints"] + schema_properties = state.get("schema_properties", {}) + valid = True + + key, value = None, None + for k, v in output_schema['properties'].items(): + if k not in schema_properties: + key = k + value = v + break + if key is None: + state = state.update( + valid_schema=valid, + schema_properties=schema_properties, + schema_processed=True) + return schema_properties, state + + response_key, response_reasoning = identify_key(key, value, input_json, None, key_hints) + + if response_key is not None: + schema_properties[key] = {"identified": True, "key": response_key, + "message": response_reasoning, + **value} + else: + schema_properties[key] = {"identified": False, "key": response_key, + "message": response_reasoning, + **value} + if key in output_schema['required']: + schema_properties[key]['required'] = True + if schema_properties[key]['identified'] == False: + valid = False + else: + schema_properties[key]['required'] = False + + state = state.update(valid_schema=valid, + schema_properties=schema_properties, + schema_processed=False) + return schema_properties, state + + +@action( + reads=["input_json", "schema_properties", "max_retries", "retry_attempts"], + writes=["max_retries_hit", "filter_query", "filters_created"] + +) +def create_jq_filter_query(state: State) -> tuple[dict, State]: + schema_properties = state["schema_properties"] + input_json = state["input_json"] + max_retries = state["max_retries"] + filtered_schema = {k: v for k, v in schema_properties.items() if v['identified'] == True} + + filter_query = state.get("filter_query") or {} + + key, value = None, None + for k, v in filtered_schema.items(): + if k not in filter_query: + key = k + value = v + break + if key is None: + state = state.update(filter_query=filter_query, filters_created=True) + return {"jq_string": None}, state + + jq_string = create_jq_string(input_json, key, value) + + if jq_string == "None": # If the response is empty, skip the key + filter_query[key] = None + state = state.update(filter_query=filter_query, filters_created=False) + return {"jq_string": None}, state + + tries = 0 + while True: + try: + jq.compile(jq_string).input(input_json).all() + break + except Exception as e: + tries += 1 + jq_string = repair_query(jq_string, str(e), input_json, None) + if tries >= max_retries: + state = state.update(max_retries_hit=True, jq_filter=None, filters_created=False) + return { + "error": f"Failed to create a valid jq filter for key '{key}' after {max_retries} retries."}, state + filter_query[key] = jq_string + state = state.update(filter_query=filter_query, max_retries_hit=False, filters_created=False) + return {"jq_string": jq_string}, state + + +@action(reads=["filter_query"], writes=["jq_filter"]) +def finalize_filter(state: State) -> tuple[dict, State]: + filter_query = state["filter_query"] + dropped_none_keys = {k: v for k, v in filter_query.items() if v is None} + complete_filter = dict_to_jq_filter(dropped_none_keys) + state = state.update(jq_filter=complete_filter) + return {"filter": complete_filter}, state + + +@action( + reads=["input_json", "jq_filter", "output_schema", "max_retries"], + writes=["max_retries_hit", "complete_filter"] +) +def validate_json(state: State) -> tuple[dict, State]: + output_schema = state["output_schema"] + complete_filter = state["jq_filter"] + input_json = state["input_json"] + max_retries = state["max_retries"] + # Validate JSON + tries = 0 + while True: + try: + result = jq.compile(complete_filter).input(input_json).all()[0] + validate(instance=result, schema=output_schema) + break + except Exception as e: + tries += 1 + if tries >= max_retries: + state = state.update(max_retries_hit=True, complete_filter=complete_filter) + return { + "error": f"Failed to validate the jq filter after {max_retries} retries."}, state + + complete_filter = repair_query(complete_filter, str(e), input_json, None) + state = state.update(complete_filter=complete_filter, max_retries_hit=False) + return {"complete_filter": complete_filter}, state + + +def translate_schema(input_json, output_schema, openai_api_key: str | None = None, key_hints=None, + max_retries=10) -> str: + app = build_application(input_json, output_schema, openai_api_key, key_hints, max_retries) + last_action, result, state = app.run(halt_after=["error_state", "good_result"]) + if last_action == "error_state": + raise RuntimeError(result) + return result["complete_filter"] + + +def build_application(input_json="", output_schema="", openai_api_key: str | None = None, key_hints=None, + max_retries=10): + app = ( + ApplicationBuilder() + .with_state( + **{ + "input_json": input_json, + "output_schema": output_schema, + "key_hints": key_hints, + "max_retries": max_retries, + } + ) + .with_actions( + # bind the vector store to the AI conversational step + validate_schema=validate_schema, + create_jq_filter_query=create_jq_filter_query, + validate_json=validate_json, + finalize_filter=finalize_filter, + error_state=burr.core.Result("complete_filter"), + good_result=burr.core.Result("complete_filter"), + ) + .with_transitions( + ("validate_schema", "validate_schema", when(schema_processed=False)), + ("validate_schema", "create_jq_filter_query", when(schema_processed=True)), + ("validate_schema", "error_state", when(valid_schema=False)), + ("create_jq_filter_query", "create_jq_filter_query", when(filters_created=False)), + ("create_jq_filter_query", "finalize_filter", when(filters_created=True)), + ("create_jq_filter_query", "error_state", when(max_retries_hit=True)), + ("finalize_filter", "validate_json", default), + ("validate_json", "error_state", when(max_retries_hit=True)), + ("validate_json", "good_result", default), + ) + .with_entrypoint("validate_schema") + .with_tracker(project="example:jaiqu") + .with_identifiers(partition_key="dagworks") + .build() + ) + app.visualize( + output_file_path="jaiqu_granular", include_conditions=True, view=True, format="png" + ) + return app + + +if __name__ == '__main__': + # app = build_application() + # app.visualize( + # output_file_path="jaiqu_granular", include_conditions=True, view=True, format="png" + # ) + schema = { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "id": { + "type": ["string", "null"], + "description": "A unique identifier for the record." + }, + "date": { + "type": "string", + "description": "A string describing the date." + }, + "model": { + "type": "string", + "description": "A text field representing the model used." + } + }, + "required": [ + "id", + "date" + ] + } + + # Provided data + input_json = { + "call.id": "123", + "datetime": "2022-01-01", + "timestamp": 1640995200, + "Address": "123 Main St", + "user": { + "name": "John Doe", + "age": 30, + "contact": "john@email.com" + } + } + + # (Optional) Create hints so the agent knows what to look for in the input + key_hints = "We are processing outputs of an containing an id, a date, and a model. All the required fields should be present in this input, but the names might be different." + + print(translate_schema(input_json, schema, key_hints=key_hints)) diff --git a/jaiqu/jaiqu_burr.py b/jaiqu/jaiqu_burr_straight_port.py similarity index 64% rename from jaiqu/jaiqu_burr.py rename to jaiqu/jaiqu_burr_straight_port.py index 43c16d7..cfe7e3c 100644 --- a/jaiqu/jaiqu_burr.py +++ b/jaiqu/jaiqu_burr_straight_port.py @@ -1,17 +1,13 @@ -import pprint -from typing import List, Optional, Tuple - import burr.core -from burr.core import Action, Application, ApplicationBuilder, State, default, expr +from burr.core import ApplicationBuilder, State, default, expr, when from burr.core.action import action -from burr.lifecycle import LifecycleAdapter, PostRunStepHook, PreRunStepHook import jq -import json from jsonschema import validate from tqdm.auto import tqdm # Use the auto submodule for notebook-friendly output if necessary from helpers import identify_key, create_jq_string, repair_query, dict_to_jq_filter + @action( reads=["input_json", "output_schema", "key_hints"], writes=["valid_schema", "schema_properties"] @@ -46,6 +42,7 @@ def validate_schema(state: State) -> tuple[dict, State]: state = state.update(valid_schema=valid, schema_properties=results) return results, state + @action( reads=["input_json", "schema_properties", "max_retries"], writes=["max_retries_hit", "jq_filter"] @@ -80,7 +77,8 @@ def create_jq_filter_query(state: State) -> tuple[dict, State]: jq_string = repair_query(jq_string, str(e), input_json, None) if tries >= max_retries: state = state.update(max_retries_hit=True, jq_filter=None) - return {"error": f"Failed to create a valid jq filter for key '{key}' after {max_retries} retries."}, state + return { + "error": f"Failed to create a valid jq filter for key '{key}' after {max_retries} retries."}, state pbar.update(1) filter_query[key] = jq_string pbar.close() @@ -89,6 +87,7 @@ def create_jq_filter_query(state: State) -> tuple[dict, State]: state = state.update(jq_filter=complete_filter, max_retries_hit=False) return {"filter": complete_filter}, state + @action( reads=["input_json", "jq_filter", "output_schema", "max_retries"], writes=["max_retries_hit", "valid_json", "complete_filter"] @@ -120,13 +119,25 @@ def validate_json(state: State) -> tuple[dict, State]: return {"complete_filter": complete_filter}, state -if __name__ == '__main__': - app = ( +def translate_schema(input_json, output_schema, openai_api_key: str | None = None, key_hints=None, + max_retries=10) -> str: + app = build_application(input_json, output_schema, openai_api_key, key_hints, max_retries) + last_action, result, state = app.run(halt_after=["error_state", "good_result"]) + if last_action == "error_state": + raise RuntimeError(result) + return result["complete_filter"] + + +def build_application(input_json="", output_schema="", openai_api_key: str | None = None, key_hints=None, + max_retries=10): + return ( ApplicationBuilder() .with_state( **{ - "input_json": "", - "output_schema": "", + "input_json": input_json, + "output_schema": output_schema, + "key_hints": key_hints, + "max_retries": max_retries, } ) .with_actions( @@ -134,23 +145,67 @@ def validate_json(state: State) -> tuple[dict, State]: validate_schema=validate_schema, create_jq_filter_query=create_jq_filter_query, validate_json=validate_json, - error_state=burr.core.Result("chat_history"), - good_result=burr.core.Result("chat_history"), + error_state=burr.core.Result("complete_filter"), + good_result=burr.core.Result("complete_filter"), ) .with_transitions( ("validate_schema", "create_jq_filter_query", default), - ("create_jq_filter_query", "validate_json", expr("'exit' in question")), - ("validate_json", "good_result", expr("valid_json")), - ("validate_schema", "good_result", expr("valid_schema")), - ("validate_schema", "error_state", expr("not valid_schema")), - ("create_jq_filter_query", "error_state", expr("max_retries_hit")), - ("validate_json", "error_state", expr("max_retries_hit")), + ("create_jq_filter_query", "validate_json", when(max_retries_hit=False)), + ("create_jq_filter_query", "error_state", when(max_retries_hit=True)), + ("validate_json", "good_result", when(valid_json=True)), + ("validate_json", "error_state", when(valid_json=False)), + ("validate_schema", "good_result", when(valid_schema=True)), + ("validate_schema", "error_state", when(valid_schema=False)), ) .with_entrypoint("validate_schema") .with_tracker(project="example:jaiqu") .with_identifiers(partition_key="dagworks") .build() ) + + +if __name__ == '__main__': + app = build_application() app.visualize( - output_file_path="jaiqu", include_conditions=True, view=True, format="png" - ) \ No newline at end of file + output_file_path="jaiqu_port", include_conditions=True, view=True, format="png" + ) + schema = { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "id": { + "type": ["string", "null"], + "description": "A unique identifier for the record." + }, + "date": { + "type": "string", + "description": "A string describing the date." + }, + "model": { + "type": "string", + "description": "A text field representing the model used." + } + }, + "required": [ + "id", + "date" + ] + } + + # Provided data + input_json = { + "call.id": "123", + "datetime": "2022-01-01", + "timestamp": 1640995200, + "Address": "123 Main St", + "user": { + "name": "John Doe", + "age": 30, + "contact": "john@email.com" + } + } + + # (Optional) Create hints so the agent knows what to look for in the input + key_hints = "We are processing outputs of an containing an id, a date, and a model. All the required fields should be present in this input, but the names might be different." + + print(translate_schema(input_json, schema, key_hints=key_hints)) diff --git a/jaiqu/jaiqu_granular.png b/jaiqu/jaiqu_granular.png new file mode 100644 index 0000000000000000000000000000000000000000..6fdb2f76cee7ec1564e078fd0b99ba3b4b06c520 GIT binary patch literal 46778 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%0Ah&q`Y*}ziSw-# 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 VK4aGTh4NjItE0QaX}kHM{|Ck-bOita literal 0 HcmV?d00001 From 9ebd30848c943fd0f404d6b9122843a9332c11be Mon Sep 17 00:00:00 2001 From: Stefan Krawczyk Date: Sun, 14 Apr 2024 21:15:48 -0700 Subject: [PATCH 3/5] Makes Jaiqu use Burr [Burr](https://github.com/dagworks-inc/burr) will enable someone to more easily debug the state of the jaiqu application if it's giving nonsense back or it hits the maxium number of retries. The design decision here is to make a close port of the original code. It could be broken down further, but that would require more work and I wasn't sure it TQDM was something that had to stay or not. This is a 100% backwards compatible change with the current API. tests have been moved to live under /tests/ and match the README. The mermaid image has been replaced with the actual application structure that Burr generates. --- .gitignore | 2 +- .idea/.gitignore | 8 - .idea/Jaiqu.iml | 12 - .idea/inspectionProfiles/Project_Default.xml | 15 -- .../inspectionProfiles/profiles_settings.xml | 6 - .idea/misc.xml | 7 - .idea/modules.xml | 8 - .idea/vcs.xml | 6 - README.md | 20 +- jaiqu/__init__.py | 2 +- jaiqu/jaiqu | 15 -- jaiqu/jaiqu.py | 230 +++++++++++++---- jaiqu/jaiqu_burr_granular.py | 237 ------------------ jaiqu/jaiqu_burr_straight_port.py | 211 ---------------- jaiqu/jaiqu_granular.png | Bin 46778 -> 0 bytes jaiqu/jaiqu_port.png | Bin 32833 -> 0 bytes jaiqu/jaiqu.png => jaiqu_app.png | Bin pyproject.toml | 6 +- requirements.txt | 2 +- {jaiqu/tests => tests}/__init__.py | 0 .../calendar/event.schema.json | 0 .../tests => tests}/calendar/gcal/input.json | 0 .../calendar/outlook/input.json | 0 .../tests => tests}/llms/anthropic/input.json | 0 .../llms/arize_openetelemetry/input.json | 0 .../tests => tests}/llms/errors.schema.json | 0 {jaiqu/tests => tests}/llms/llms.schema.json | 0 .../tests => tests}/llms/openai/schema.json | 0 tests/test_jaiqu.py | 56 +++++ 29 files changed, 245 insertions(+), 598 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/Jaiqu.iml delete mode 100644 .idea/inspectionProfiles/Project_Default.xml delete mode 100644 .idea/inspectionProfiles/profiles_settings.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/vcs.xml delete mode 100644 jaiqu/jaiqu delete mode 100644 jaiqu/jaiqu_burr_granular.py delete mode 100644 jaiqu/jaiqu_burr_straight_port.py delete mode 100644 jaiqu/jaiqu_granular.png delete mode 100644 jaiqu/jaiqu_port.png rename jaiqu/jaiqu.png => jaiqu_app.png (100%) rename {jaiqu/tests => tests}/__init__.py (100%) rename {jaiqu/tests => tests}/calendar/event.schema.json (100%) rename {jaiqu/tests => tests}/calendar/gcal/input.json (100%) rename {jaiqu/tests => tests}/calendar/outlook/input.json (100%) rename {jaiqu/tests => tests}/llms/anthropic/input.json (100%) rename {jaiqu/tests => tests}/llms/arize_openetelemetry/input.json (100%) rename {jaiqu/tests => tests}/llms/errors.schema.json (100%) rename {jaiqu/tests => tests}/llms/llms.schema.json (100%) rename {jaiqu/tests => tests}/llms/openai/schema.json (100%) create mode 100644 tests/test_jaiqu.py diff --git a/.gitignore b/.gitignore index 68bc17f..2dc53ca 100644 --- a/.gitignore +++ b/.gitignore @@ -157,4 +157,4 @@ cython_debug/ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ +.idea/ diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 13566b8..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/.idea/Jaiqu.iml b/.idea/Jaiqu.iml deleted file mode 100644 index 8b8c395..0000000 --- a/.idea/Jaiqu.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index d5fa4af..0000000 --- a/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml deleted file mode 100644 index 105ce2d..0000000 --- a/.idea/inspectionProfiles/profiles_settings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index d3511d7..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index bee6e19..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1dd..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/README.md b/README.md index 31011f6..1da1f40 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,7 @@ print(valid) Creating a repeatable jq query for extracitng data from identically formatted input JSONs ```python -jq_query = jaiqu.translate_schema(input_json, schema, key_hints, max_retries=30) +jq_query = translate_schema(input_json, schema, key_hints, max_retries=30) >>>'{"id": .attributes["call.id"], "date": .datetime}' ``` @@ -137,22 +137,8 @@ pip install jaiqu ## Architecture -Unraveling the Jaiqu agentic workflow pattern -```mermaid -flowchart TD - A[Start translate_schema] --> B{Validate input schema} - B -- Valid --> C[For each key, create a jq filter query] - B -- Invalid --> D[Throw RuntimeError] - C --> E[Compile and Test jq Filter] - E -- Success --> F[Validate JSON] - E -- Fail --> G[Retry Create jq Filter] - G -- Success --> E - G -- Fail n times--> H[Throw RuntimeError] - F -- Success --> I[Return jq query string] - F -- Fail --> J[Retry Validate JSON] - J -- Success --> I - J -- Fail n times --> K[Throw RuntimeError] -``` +Unraveling the Jaiqu agentic workflow pattern: +![jaiqu](jaiqu_app.png) ## Running tests diff --git a/jaiqu/__init__.py b/jaiqu/__init__.py index d28ffc6..f2b881e 100644 --- a/jaiqu/__init__.py +++ b/jaiqu/__init__.py @@ -1 +1 @@ -# from .jaiqu import validate_schema, translate_schema +from .jaiqu import translate_schema, validate_schema diff --git a/jaiqu/jaiqu b/jaiqu/jaiqu deleted file mode 100644 index 5abaa7f..0000000 --- a/jaiqu/jaiqu +++ /dev/null @@ -1,15 +0,0 @@ -digraph { - graph [compound=false concentrate=false rankdir=TB ranksep=0.4] - validate_schema [label=validate_schema shape=box style=rounded] - create_jq_filter_query [label=create_jq_filter_query shape=box style=rounded] - validate_json [label=validate_json shape=box style=rounded] - error_state [label=error_state shape=box style=rounded] - good_result [label=good_result shape=box style=rounded] - validate_schema -> create_jq_filter_query [style=solid] - create_jq_filter_query -> validate_json [label="'exit' in question" style=dashed] - validate_json -> good_result [label=valid_json style=dashed] - validate_schema -> good_result [label=valid_schema style=dashed] - validate_schema -> error_state [label="not valid_schema" style=dashed] - create_jq_filter_query -> error_state [label=max_retries_hit style=dashed] - validate_json -> error_state [label=max_retries_hit style=dashed] -} diff --git a/jaiqu/jaiqu.py b/jaiqu/jaiqu.py index 1d7f591..b317926 100644 --- a/jaiqu/jaiqu.py +++ b/jaiqu/jaiqu.py @@ -1,12 +1,19 @@ +import logging +import os + +import burr.core +from burr.core import ApplicationBuilder, State, default, expr, when +from burr.core.action import action + import jq -import json from jsonschema import validate from tqdm.auto import tqdm # Use the auto submodule for notebook-friendly output if necessary -from helpers import identify_key, create_jq_string, repair_query, dict_to_jq_filter +from .helpers import identify_key, create_jq_string, repair_query, dict_to_jq_filter + +logger = logging.getLogger(__name__) -def validate_schema(input_json: dict, output_schema: dict, openai_api_key: str | None = None, key_hints=None) -> tuple[ - dict, bool]: +def validate_schema(input_json, output_schema, openai_api_key: str | None = None, key_hints=None, quiet=False): """Validates the schema of the input JSON against the output schema. Args: input_json (dict): The input JSON parsed into a dictionary. @@ -15,12 +22,12 @@ def validate_schema(input_json: dict, output_schema: dict, openai_api_key: str | key_hints (any, optional): Key hints to assist in identifying keys. Defaults to None. Returns: - tuple[dict, bool]: A tuple containing the results of the validation and a boolean indicating if the validation was successful. + tuple[dict, bool]: A tuple containing the results of the validation and a boolean indicating if the + validation was successful. """ - results = {} valid = True - with tqdm(total=len(output_schema['properties']), desc="Validating schema") as pbar: + with tqdm(total=len(output_schema['properties']), desc="Validating schema", disable=quiet) as pbar: for key, value in output_schema['properties'].items(): pbar.set_postfix_str(f"Key: {key}", refresh=True) response_key, response_reasoning = identify_key(key, value, input_json, openai_api_key, key_hints) @@ -40,47 +47,45 @@ def validate_schema(input_json: dict, output_schema: dict, openai_api_key: str | else: results[key]['required'] = False pbar.update(1) - - return results, valid - - -def translate_schema(input_json, output_schema, openai_api_key: str | None = None, key_hints=None, - max_retries=10) -> str: - """ - Translate the input JSON schema into a filtering query using jq. - - Args: - input_json (dict): The input JSON to be reformatted. - output_schema (dict): The desired output schema using standard schema formatting. - openai_api_key (str, optional): OpenAI API key. Defaults to None. - key_hints (None, optional): Hints for translating keys. Defaults to None. - max_retries (int, optional): Maximum number of retries for creating a valid jq filter. Defaults to 10. - - Returns: - str: The filtering query in jq syntax. - - Raises: - RuntimeError: If the input JSON does not contain the required data to satisfy the output schema. - RuntimeError: If failed to create a valid jq filter after maximum retries. - RuntimeError: If failed to validate the jq filter after maximum retries. - """ - - schema_properties, is_valid = validate_schema(input_json, output_schema, key_hints=key_hints, - openai_api_key=openai_api_key) - if not is_valid: - raise RuntimeError( - f"The input JSON does not contain the required data to satisfy the output schema: \n\n{json.dumps(schema_properties, indent=2)}") - + return results, valid + + +@action( + reads=["input_json", "output_schema", "key_hints", "quiet"], + writes=["valid_schema", "schema_properties"] +) +def validate_schema_action(state: State) -> tuple[dict, State]: + """Action to validate the provided input schema.""" + output_schema = state["output_schema"] + input_json = state["input_json"] + key_hints = state["key_hints"] + quiet = state.get("quiet", False) + results, valid = validate_schema(input_json, output_schema, key_hints=key_hints, openai_api_key=None, quiet=quiet) + state = state.update(valid_schema=valid, schema_properties=results) + return results, state + + +@action( + reads=["input_json", "schema_properties", "max_retries", "quiet"], + writes=["max_retries_hit", "jq_filter"] +) +def create_jq_filter_query(state: State) -> tuple[dict, State]: + """Creates the JQ filter query.""" + schema_properties = state["schema_properties"] + input_json = state["input_json"] + max_retries = state["max_retries"] filtered_schema = {k: v for k, v in schema_properties.items() if v['identified'] == True} - + quiet = state.get("quiet", False) filter_query = {} with tqdm(total=len(filtered_schema), - desc="Translating schema") as pbar, tqdm(total=max_retries, - desc="Retry attempts") as pbar_retries: + desc="Translating schema", + disable=quiet) as pbar, tqdm(total=max_retries, + desc="Retry attempts", + disable=quiet) as pbar_retries: for key, value in filtered_schema.items(): pbar.set_postfix_str(f"Key: {key}", refresh=True) - jq_string = create_jq_string(input_json, key, value, openai_api_key) + jq_string = create_jq_string(input_json, key, value) if jq_string == "None": # If the response is empty, skip the key pbar.update(1) @@ -89,23 +94,39 @@ def translate_schema(input_json, output_schema, openai_api_key: str | None = Non tries = 0 while True: try: - key_query = jq.compile(jq_string).input(input_json).all() + jq.compile(jq_string).input(input_json).all() break except Exception as e: tries += 1 pbar_retries.update(1) - jq_string = repair_query(jq_string, str(e), input_json, openai_api_key) + jq_string = repair_query(jq_string, str(e), input_json, None) if tries >= max_retries: - raise RuntimeError( - f"Failed to create a valid jq filter for key '{key}' after {max_retries} retries.") + state = state.update(max_retries_hit=True, jq_filter=None) + return { + "error": f"Failed to create a valid jq filter for key '{key}' after {max_retries} retries."}, state pbar.update(1) filter_query[key] = jq_string pbar.close() pbar_retries.close() complete_filter = dict_to_jq_filter(filter_query) + state = state.update(jq_filter=complete_filter, max_retries_hit=False) + return {"filter": complete_filter}, state + + +@action( + reads=["input_json", "jq_filter", "output_schema", "max_retries", "quiet"], + writes=["max_retries_hit", "valid_json", "complete_filter"] +) +def validate_json(state: State) -> tuple[dict, State]: + """Validates the filter JSON.""" + output_schema = state["output_schema"] + complete_filter = state["jq_filter"] + input_json = state["input_json"] + max_retries = state["max_retries"] + quiet = state.get("quiet", False) # Validate JSON tries = 0 - with tqdm(total=max_retries, desc="Validation attempts") as pbar_validation: + with tqdm(total=max_retries, desc="Validation attempts", disable=quiet) as pbar_validation: while True: try: result = jq.compile(complete_filter).input(input_json).all()[0] @@ -116,7 +137,114 @@ def translate_schema(input_json, output_schema, openai_api_key: str | None = Non tries += 1 pbar_validation.update(1) if tries >= max_retries: - raise RuntimeError(f"Failed to validate the jq filter after {max_retries} retries.") - complete_filter = repair_query(complete_filter, str(e), input_json, openai_api_key) - pbar_validation.close() - return complete_filter + state = state.update(max_retries_hit=True, valid_json=False, complete_filter=complete_filter) + return { + "error": f"Failed to validate the jq filter after {max_retries} retries."}, state + + complete_filter = repair_query(complete_filter, str(e), input_json, None) + state = state.update(complete_filter=complete_filter, max_retries_hit=False, valid_json=True) + return {"complete_filter": complete_filter}, state + + +def translate_schema(input_json, output_schema, openai_api_key: str | None = None, key_hints=None, + max_retries=10, quiet=False, save_trace: bool = False) -> str: + """ + Translate the input JSON schema into a filtering query using jq. + + Args: + input_json (dict): The input JSON to be reformatted. + output_schema (dict): The desired output schema using standard schema formatting. + openai_api_key (str, optional): OpenAI API key. Defaults to None. + key_hints (None, optional): Hints for translating keys. Defaults to None. + max_retries (int, optional): Maximum number of retries for creating a valid jq filter. Defaults to 10. + quiet (bool, optional): Quiet mode to turn off TQDM progress bars. Defaults to False. + save_trace (bool, optional): turn on Burr tracking to debug jaiqu runs. Defaults to False. + + Returns: + str: The filtering query in jq syntax. + + Raises: + RuntimeError: If the input JSON does not contain the required data to satisfy the output schema. + RuntimeError: If failed to create a valid jq filter after maximum retries. + RuntimeError: If failed to validate the jq filter after maximum retries. + """ + if openai_api_key is not None: + os.environ["OPENAI_API_KEY"] = openai_api_key + app = build_application(input_json, output_schema, + key_hints=key_hints, max_retries=max_retries, quiet=quiet, save_trace=save_trace) + last_action, result, state = app.run(halt_after=["error_state", "good_result"]) + if last_action == "error_state": + raise RuntimeError(result) + return result["complete_filter"] + + +def build_application(input_json, + output_schema, + key_hints: str = None, + max_retries: int = 10, + quiet: bool = False, + save_trace: bool = False, + visualize: bool = False): + """ + Builds the application for translating the input JSON schema into a filtering query using jq. + + Args: + input_json (dict): The input JSON to be reformatted. + output_schema (dict): The desired output schema using standard schema formatting. + key_hints (str, optional): Hints for translating keys. Defaults to None. + max_retries (int, optional): Maximum number of retries for creating a valid jq filter. Defaults to 10. + quiet (bool, optional): Quiet mode to turn off TQDM progress bars. Defaults to False. + save_trace (bool, optional): Turn on Burr tracking to debug jaiqu runs. Defaults to False. + visualize (bool, optional): If set to True, visualizes the application flow. Defaults to False. + + Returns: + Application: The built application with the specified state, actions, and transitions. + """ + _app = ( + ApplicationBuilder() + .with_state( + **{ + "input_json": input_json, + "output_schema": output_schema, + "key_hints": key_hints, + "max_retries": max_retries, + "quiet": quiet, + } + ) + .with_actions( + # bind the vector store to the AI conversational step + validate_schema=validate_schema_action, + create_jq_filter_query=create_jq_filter_query, + validate_json=validate_json, + error_state=burr.core.Result("complete_filter"), + good_result=burr.core.Result("complete_filter"), + ) + .with_transitions( + ("validate_schema", "create_jq_filter_query", default), + ("create_jq_filter_query", "validate_json", when(max_retries_hit=False)), + ("create_jq_filter_query", "error_state", when(max_retries_hit=True)), + ("validate_json", "good_result", when(valid_json=True)), + ("validate_json", "error_state", when(valid_json=False)), + ("validate_schema", "good_result", when(valid_schema=True)), + ("validate_schema", "error_state", when(valid_schema=False)), + ) + .with_entrypoint("validate_schema") + ) + if save_trace: + logger.warning("To see trace information, start `burr` (pip install \"burr[start]\") in a separate terminal" + " and go to http://localhost:7241") + _app = _app.with_tracker(project="jaiqu") + _app = _app.build() + if visualize: + _app.visualize( + output_file_path="jaiqu_app", include_conditions=True, view=False, format="png" + ) + return _app + + +if __name__ == '__main__': + """Recreate the image easily""" + app = build_application("", "") + app.visualize( + output_file_path="jaiqu_app", include_conditions=True, view=False, format="png" + ) diff --git a/jaiqu/jaiqu_burr_granular.py b/jaiqu/jaiqu_burr_granular.py deleted file mode 100644 index c78991e..0000000 --- a/jaiqu/jaiqu_burr_granular.py +++ /dev/null @@ -1,237 +0,0 @@ -import burr.core -from burr.core import ApplicationBuilder, State, default, when -from burr.core.action import action - -import jq -from jsonschema import validate -from helpers import identify_key, create_jq_string, repair_query, dict_to_jq_filter - - -@action( - reads=["input_json", "output_schema", "key_hints", "schema_properties"], - writes=["valid_schema", "schema_properties", "schema_processed"] -) -def validate_schema(state: State) -> tuple[dict, State]: - output_schema = state["output_schema"] - input_json = state["input_json"] - key_hints = state["key_hints"] - schema_properties = state.get("schema_properties", {}) - valid = True - - key, value = None, None - for k, v in output_schema['properties'].items(): - if k not in schema_properties: - key = k - value = v - break - if key is None: - state = state.update( - valid_schema=valid, - schema_properties=schema_properties, - schema_processed=True) - return schema_properties, state - - response_key, response_reasoning = identify_key(key, value, input_json, None, key_hints) - - if response_key is not None: - schema_properties[key] = {"identified": True, "key": response_key, - "message": response_reasoning, - **value} - else: - schema_properties[key] = {"identified": False, "key": response_key, - "message": response_reasoning, - **value} - if key in output_schema['required']: - schema_properties[key]['required'] = True - if schema_properties[key]['identified'] == False: - valid = False - else: - schema_properties[key]['required'] = False - - state = state.update(valid_schema=valid, - schema_properties=schema_properties, - schema_processed=False) - return schema_properties, state - - -@action( - reads=["input_json", "schema_properties", "max_retries", "retry_attempts"], - writes=["max_retries_hit", "filter_query", "filters_created"] - -) -def create_jq_filter_query(state: State) -> tuple[dict, State]: - schema_properties = state["schema_properties"] - input_json = state["input_json"] - max_retries = state["max_retries"] - filtered_schema = {k: v for k, v in schema_properties.items() if v['identified'] == True} - - filter_query = state.get("filter_query") or {} - - key, value = None, None - for k, v in filtered_schema.items(): - if k not in filter_query: - key = k - value = v - break - if key is None: - state = state.update(filter_query=filter_query, filters_created=True) - return {"jq_string": None}, state - - jq_string = create_jq_string(input_json, key, value) - - if jq_string == "None": # If the response is empty, skip the key - filter_query[key] = None - state = state.update(filter_query=filter_query, filters_created=False) - return {"jq_string": None}, state - - tries = 0 - while True: - try: - jq.compile(jq_string).input(input_json).all() - break - except Exception as e: - tries += 1 - jq_string = repair_query(jq_string, str(e), input_json, None) - if tries >= max_retries: - state = state.update(max_retries_hit=True, jq_filter=None, filters_created=False) - return { - "error": f"Failed to create a valid jq filter for key '{key}' after {max_retries} retries."}, state - filter_query[key] = jq_string - state = state.update(filter_query=filter_query, max_retries_hit=False, filters_created=False) - return {"jq_string": jq_string}, state - - -@action(reads=["filter_query"], writes=["jq_filter"]) -def finalize_filter(state: State) -> tuple[dict, State]: - filter_query = state["filter_query"] - dropped_none_keys = {k: v for k, v in filter_query.items() if v is None} - complete_filter = dict_to_jq_filter(dropped_none_keys) - state = state.update(jq_filter=complete_filter) - return {"filter": complete_filter}, state - - -@action( - reads=["input_json", "jq_filter", "output_schema", "max_retries"], - writes=["max_retries_hit", "complete_filter"] -) -def validate_json(state: State) -> tuple[dict, State]: - output_schema = state["output_schema"] - complete_filter = state["jq_filter"] - input_json = state["input_json"] - max_retries = state["max_retries"] - # Validate JSON - tries = 0 - while True: - try: - result = jq.compile(complete_filter).input(input_json).all()[0] - validate(instance=result, schema=output_schema) - break - except Exception as e: - tries += 1 - if tries >= max_retries: - state = state.update(max_retries_hit=True, complete_filter=complete_filter) - return { - "error": f"Failed to validate the jq filter after {max_retries} retries."}, state - - complete_filter = repair_query(complete_filter, str(e), input_json, None) - state = state.update(complete_filter=complete_filter, max_retries_hit=False) - return {"complete_filter": complete_filter}, state - - -def translate_schema(input_json, output_schema, openai_api_key: str | None = None, key_hints=None, - max_retries=10) -> str: - app = build_application(input_json, output_schema, openai_api_key, key_hints, max_retries) - last_action, result, state = app.run(halt_after=["error_state", "good_result"]) - if last_action == "error_state": - raise RuntimeError(result) - return result["complete_filter"] - - -def build_application(input_json="", output_schema="", openai_api_key: str | None = None, key_hints=None, - max_retries=10): - app = ( - ApplicationBuilder() - .with_state( - **{ - "input_json": input_json, - "output_schema": output_schema, - "key_hints": key_hints, - "max_retries": max_retries, - } - ) - .with_actions( - # bind the vector store to the AI conversational step - validate_schema=validate_schema, - create_jq_filter_query=create_jq_filter_query, - validate_json=validate_json, - finalize_filter=finalize_filter, - error_state=burr.core.Result("complete_filter"), - good_result=burr.core.Result("complete_filter"), - ) - .with_transitions( - ("validate_schema", "validate_schema", when(schema_processed=False)), - ("validate_schema", "create_jq_filter_query", when(schema_processed=True)), - ("validate_schema", "error_state", when(valid_schema=False)), - ("create_jq_filter_query", "create_jq_filter_query", when(filters_created=False)), - ("create_jq_filter_query", "finalize_filter", when(filters_created=True)), - ("create_jq_filter_query", "error_state", when(max_retries_hit=True)), - ("finalize_filter", "validate_json", default), - ("validate_json", "error_state", when(max_retries_hit=True)), - ("validate_json", "good_result", default), - ) - .with_entrypoint("validate_schema") - .with_tracker(project="example:jaiqu") - .with_identifiers(partition_key="dagworks") - .build() - ) - app.visualize( - output_file_path="jaiqu_granular", include_conditions=True, view=True, format="png" - ) - return app - - -if __name__ == '__main__': - # app = build_application() - # app.visualize( - # output_file_path="jaiqu_granular", include_conditions=True, view=True, format="png" - # ) - schema = { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "properties": { - "id": { - "type": ["string", "null"], - "description": "A unique identifier for the record." - }, - "date": { - "type": "string", - "description": "A string describing the date." - }, - "model": { - "type": "string", - "description": "A text field representing the model used." - } - }, - "required": [ - "id", - "date" - ] - } - - # Provided data - input_json = { - "call.id": "123", - "datetime": "2022-01-01", - "timestamp": 1640995200, - "Address": "123 Main St", - "user": { - "name": "John Doe", - "age": 30, - "contact": "john@email.com" - } - } - - # (Optional) Create hints so the agent knows what to look for in the input - key_hints = "We are processing outputs of an containing an id, a date, and a model. All the required fields should be present in this input, but the names might be different." - - print(translate_schema(input_json, schema, key_hints=key_hints)) diff --git a/jaiqu/jaiqu_burr_straight_port.py b/jaiqu/jaiqu_burr_straight_port.py deleted file mode 100644 index cfe7e3c..0000000 --- a/jaiqu/jaiqu_burr_straight_port.py +++ /dev/null @@ -1,211 +0,0 @@ -import burr.core -from burr.core import ApplicationBuilder, State, default, expr, when -from burr.core.action import action - -import jq -from jsonschema import validate -from tqdm.auto import tqdm # Use the auto submodule for notebook-friendly output if necessary -from helpers import identify_key, create_jq_string, repair_query, dict_to_jq_filter - - -@action( - reads=["input_json", "output_schema", "key_hints"], - writes=["valid_schema", "schema_properties"] -) -def validate_schema(state: State) -> tuple[dict, State]: - output_schema = state["output_schema"] - input_json = state["input_json"] - key_hints = state["key_hints"] - results = {} - valid = True - with tqdm(total=len(output_schema['properties']), desc="Validating schema") as pbar: - for key, value in output_schema['properties'].items(): - pbar.set_postfix_str(f"Key: {key}", refresh=True) - response_key, response_reasoning = identify_key(key, value, input_json, None, key_hints) - - if response_key is not None: - results[key] = {"identified": True, "key": response_key, - "message": response_reasoning, - **value} - else: - results[key] = {"identified": False, "key": response_key, - "message": response_reasoning, - **value} - if key in output_schema['required']: - results[key]['required'] = True - if results[key]['identified'] == False: - valid = False - else: - results[key]['required'] = False - pbar.update(1) - - state = state.update(valid_schema=valid, schema_properties=results) - return results, state - - -@action( - reads=["input_json", "schema_properties", "max_retries"], - writes=["max_retries_hit", "jq_filter"] -) -def create_jq_filter_query(state: State) -> tuple[dict, State]: - schema_properties = state["schema_properties"] - input_json = state["input_json"] - max_retries = state["max_retries"] - filtered_schema = {k: v for k, v in schema_properties.items() if v['identified'] == True} - - filter_query = {} - - with tqdm(total=len(filtered_schema), - desc="Translating schema") as pbar, tqdm(total=max_retries, - desc="Retry attempts") as pbar_retries: - for key, value in filtered_schema.items(): - pbar.set_postfix_str(f"Key: {key}", refresh=True) - jq_string = create_jq_string(input_json, key, value) - - if jq_string == "None": # If the response is empty, skip the key - pbar.update(1) - continue - - tries = 0 - while True: - try: - jq.compile(jq_string).input(input_json).all() - break - except Exception as e: - tries += 1 - pbar_retries.update(1) - jq_string = repair_query(jq_string, str(e), input_json, None) - if tries >= max_retries: - state = state.update(max_retries_hit=True, jq_filter=None) - return { - "error": f"Failed to create a valid jq filter for key '{key}' after {max_retries} retries."}, state - pbar.update(1) - filter_query[key] = jq_string - pbar.close() - pbar_retries.close() - complete_filter = dict_to_jq_filter(filter_query) - state = state.update(jq_filter=complete_filter, max_retries_hit=False) - return {"filter": complete_filter}, state - - -@action( - reads=["input_json", "jq_filter", "output_schema", "max_retries"], - writes=["max_retries_hit", "valid_json", "complete_filter"] -) -def validate_json(state: State) -> tuple[dict, State]: - output_schema = state["output_schema"] - complete_filter = state["jq_filter"] - input_json = state["input_json"] - max_retries = state["max_retries"] - # Validate JSON - tries = 0 - with tqdm(total=max_retries, desc="Validation attempts") as pbar_validation: - while True: - try: - result = jq.compile(complete_filter).input(input_json).all()[0] - validate(instance=result, schema=output_schema) - pbar_validation.close() - break - except Exception as e: - tries += 1 - pbar_validation.update(1) - if tries >= max_retries: - state = state.update(max_retries_hit=True, valid_json=False, complete_filter=complete_filter) - return { - "error": f"Failed to validate the jq filter after {max_retries} retries."}, state - - complete_filter = repair_query(complete_filter, str(e), input_json, None) - state = state.update(complete_filter=complete_filter, max_retries_hit=False, valid_json=True) - return {"complete_filter": complete_filter}, state - - -def translate_schema(input_json, output_schema, openai_api_key: str | None = None, key_hints=None, - max_retries=10) -> str: - app = build_application(input_json, output_schema, openai_api_key, key_hints, max_retries) - last_action, result, state = app.run(halt_after=["error_state", "good_result"]) - if last_action == "error_state": - raise RuntimeError(result) - return result["complete_filter"] - - -def build_application(input_json="", output_schema="", openai_api_key: str | None = None, key_hints=None, - max_retries=10): - return ( - ApplicationBuilder() - .with_state( - **{ - "input_json": input_json, - "output_schema": output_schema, - "key_hints": key_hints, - "max_retries": max_retries, - } - ) - .with_actions( - # bind the vector store to the AI conversational step - validate_schema=validate_schema, - create_jq_filter_query=create_jq_filter_query, - validate_json=validate_json, - error_state=burr.core.Result("complete_filter"), - good_result=burr.core.Result("complete_filter"), - ) - .with_transitions( - ("validate_schema", "create_jq_filter_query", default), - ("create_jq_filter_query", "validate_json", when(max_retries_hit=False)), - ("create_jq_filter_query", "error_state", when(max_retries_hit=True)), - ("validate_json", "good_result", when(valid_json=True)), - ("validate_json", "error_state", when(valid_json=False)), - ("validate_schema", "good_result", when(valid_schema=True)), - ("validate_schema", "error_state", when(valid_schema=False)), - ) - .with_entrypoint("validate_schema") - .with_tracker(project="example:jaiqu") - .with_identifiers(partition_key="dagworks") - .build() - ) - - -if __name__ == '__main__': - app = build_application() - app.visualize( - output_file_path="jaiqu_port", include_conditions=True, view=True, format="png" - ) - schema = { - "$schema": "http://json-schema.org/draft-07/schema#", - "type": "object", - "properties": { - "id": { - "type": ["string", "null"], - "description": "A unique identifier for the record." - }, - "date": { - "type": "string", - "description": "A string describing the date." - }, - "model": { - "type": "string", - "description": "A text field representing the model used." - } - }, - "required": [ - "id", - "date" - ] - } - - # Provided data - input_json = { - "call.id": "123", - "datetime": "2022-01-01", - "timestamp": 1640995200, - "Address": "123 Main St", - "user": { - "name": "John Doe", - "age": 30, - "contact": "john@email.com" - } - } - - # (Optional) Create hints so the agent knows what to look for in the input - key_hints = "We are processing outputs of an containing an id, a date, and a model. All the required fields should be present in this input, but the names might be different." - - print(translate_schema(input_json, schema, key_hints=key_hints)) diff --git a/jaiqu/jaiqu_granular.png b/jaiqu/jaiqu_granular.png deleted file mode 100644 index 6fdb2f76cee7ec1564e078fd0b99ba3b4b06c520..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 46778 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%0Ah&q`Y*}ziSw-# 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 VK4aGTh4NjItE0QaX}kHM{|Ck-bOita diff --git a/jaiqu/jaiqu.png b/jaiqu_app.png similarity index 100% rename from jaiqu/jaiqu.png rename to jaiqu_app.png diff --git a/pyproject.toml b/pyproject.toml index 56fc039..576b3a7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "jaiqu" -version = "0.0.4" +version = "0.0.5" authors = [ { name = "Alex Reibman", email = "areibman@gmail.com" }, { name = "Howard Gil", email = "howardbgil@gmail.com" }, @@ -21,7 +21,8 @@ classifiers = [ dependencies = [ "jq==1.6.0", "openai~=1.12.0", - "jsonschema==4.21.1" + "jsonschema==4.21.1", + "burr~=0.*", ] [project.optional-dependencies] @@ -29,6 +30,7 @@ dev = [ "pytest>=7.4.4", "flake8>=3.1.0", "coverage[toml]>=7.4.0", + "burr[start]~=0.*", ] [project.urls] diff --git a/requirements.txt b/requirements.txt index cbe6978..d20cfe2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ jq==1.6.0 openai>=1.12.0,<2.0.0 jsonschema==4.21.1 -burr \ No newline at end of file +burr~=0.* # install everything until 1.0.0 \ No newline at end of file diff --git a/jaiqu/tests/__init__.py b/tests/__init__.py similarity index 100% rename from jaiqu/tests/__init__.py rename to tests/__init__.py diff --git a/jaiqu/tests/calendar/event.schema.json b/tests/calendar/event.schema.json similarity index 100% rename from jaiqu/tests/calendar/event.schema.json rename to tests/calendar/event.schema.json diff --git a/jaiqu/tests/calendar/gcal/input.json b/tests/calendar/gcal/input.json similarity index 100% rename from jaiqu/tests/calendar/gcal/input.json rename to tests/calendar/gcal/input.json diff --git a/jaiqu/tests/calendar/outlook/input.json b/tests/calendar/outlook/input.json similarity index 100% rename from jaiqu/tests/calendar/outlook/input.json rename to tests/calendar/outlook/input.json diff --git a/jaiqu/tests/llms/anthropic/input.json b/tests/llms/anthropic/input.json similarity index 100% rename from jaiqu/tests/llms/anthropic/input.json rename to tests/llms/anthropic/input.json diff --git a/jaiqu/tests/llms/arize_openetelemetry/input.json b/tests/llms/arize_openetelemetry/input.json similarity index 100% rename from jaiqu/tests/llms/arize_openetelemetry/input.json rename to tests/llms/arize_openetelemetry/input.json diff --git a/jaiqu/tests/llms/errors.schema.json b/tests/llms/errors.schema.json similarity index 100% rename from jaiqu/tests/llms/errors.schema.json rename to tests/llms/errors.schema.json diff --git a/jaiqu/tests/llms/llms.schema.json b/tests/llms/llms.schema.json similarity index 100% rename from jaiqu/tests/llms/llms.schema.json rename to tests/llms/llms.schema.json diff --git a/jaiqu/tests/llms/openai/schema.json b/tests/llms/openai/schema.json similarity index 100% rename from jaiqu/tests/llms/openai/schema.json rename to tests/llms/openai/schema.json diff --git a/tests/test_jaiqu.py b/tests/test_jaiqu.py new file mode 100644 index 0000000..1e3da40 --- /dev/null +++ b/tests/test_jaiqu.py @@ -0,0 +1,56 @@ +import jq +from jaiqu import translate_schema + + +def test_translate_schema(): + schema = { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "id": { + "type": ["string", "null"], + "description": "A unique identifier for the record." + }, + "date": { + "type": "string", + "description": "A string describing the date." + }, + "model": { + "type": "string", + "description": "A text field representing the model used." + } + }, + "required": [ + "id", + "date" + ] + } + + # Provided data + input_json = { + "call.id": "123", + "datetime": "2022-01-01", + "timestamp": 1640995200, + "Address": "123 Main St", + "user": { + "name": "John Doe", + "age": 30, + "contact": "john@email.com" + } + } + + # (Optional) Create hints so the agent knows what to look for in the input + key_hints = "We are processing outputs of an containing an id, a date, and a model. All the required fields should be present in this input, but the names might be different." + + new_schema = translate_schema(input_json, schema, key_hints=key_hints) + # there are many permutations possible so we check the result rather than the schema... + # some_possible_schemas = [ + # '{ "id": (.["call.id"] // "None"), "date": (.datetime // "None") }', + # '{ "id": .["call.id"], "date": (.datetime // "None") }', + # '{ "id": .["call.id"] // "None", "date": .datetime // null }', + # '{ "id": (."call.id"? // "None"), "date": (.datetime? // "None") }', + # '{ "id": .["call.id"] // "None", "date": .datetime // "None" }', + # '{ "id": (.["call.id"] // "None"), "date": (.datetime // null) }' + # ] + actual = jq.compile(new_schema).input(input_json).all() + assert actual == [{'id': '123', 'date': '2022-01-01'}] From 67347a7d73a1942d459fe63aed6560dd3086dbda Mon Sep 17 00:00:00 2001 From: Stefan Krawczyk Date: Sun, 14 Apr 2024 21:34:08 -0700 Subject: [PATCH 4/5] Adds save_trace option to CLI So that the CLI has parity with the Burr changes. --- jaiqu/cli.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/jaiqu/cli.py b/jaiqu/cli.py index 44bf3b5..05facd2 100644 --- a/jaiqu/cli.py +++ b/jaiqu/cli.py @@ -30,6 +30,12 @@ def jaiqu( "--max-retries", help="Max number of retries for the ai to complete the task", ), + save_trace: bool = Option( + False, + "-s", + "--save-trace", + help="Saves a trace for introspection using the Burr UI.", + ), ): """ Validate and translate a json schema to jq filter @@ -53,7 +59,8 @@ def jaiqu( input_json=input_json, key_hints=key_hints, max_retries=max_retries, - quiet=quiet + quiet=quiet, + save_trace=save_trace, ) print(query) From 7b4c3460bb10e3262e57d5bb0672581e8c19788a Mon Sep 17 00:00:00 2001 From: Stefan Krawczyk Date: Sun, 14 Apr 2024 21:36:52 -0700 Subject: [PATCH 5/5] Fixes up typing and docs on tranlate_schema Missed this in the merge. --- jaiqu/jaiqu.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/jaiqu/jaiqu.py b/jaiqu/jaiqu.py index a0dd7c1..273bd7f 100644 --- a/jaiqu/jaiqu.py +++ b/jaiqu/jaiqu.py @@ -13,7 +13,8 @@ logger = logging.getLogger(__name__) -def validate_schema(input_json: dict, output_schema: dict, openai_api_key: str | None = None, key_hints=None, quiet=False) -> tuple[dict, bool]: +def validate_schema(input_json: dict, output_schema: dict, openai_api_key: str | None = None, key_hints=None, + quiet=False) -> tuple[dict, bool]: """Validates the schema of the input JSON against the output schema. Args: input_json (dict): The input JSON parsed into a dictionary. @@ -147,8 +148,8 @@ def validate_json(state: State) -> tuple[dict, State]: return {"complete_filter": complete_filter}, state -def translate_schema(input_json, output_schema, openai_api_key: str | None = None, key_hints=None, - max_retries=10, quiet=False, save_trace: bool = False) -> str: +def translate_schema(input_json: dict, output_schema: dict, openai_api_key: str | None = None, key_hints: str = None, + max_retries: int = 10, quiet: bool = False, save_trace: bool = False) -> str: """ Translate the input JSON schema into a filtering query using jq. @@ -156,7 +157,7 @@ def translate_schema(input_json, output_schema, openai_api_key: str | None = Non input_json (dict): The input JSON to be reformatted. output_schema (dict): The desired output schema using standard schema formatting. openai_api_key (str, optional): OpenAI API key. Defaults to None. - key_hints (None, optional): Hints for translating keys. Defaults to None. + key_hints (str, optional): Hints for translating keys. Defaults to None. max_retries (int, optional): Maximum number of retries for creating a valid jq filter. Defaults to 10. quiet (bool, optional): Quiet mode to turn off TQDM progress bars. Defaults to False. save_trace (bool, optional): turn on Burr tracking to debug jaiqu runs. Defaults to False.