From 92964d41f69781058130e014848bb20a9cd8e439 Mon Sep 17 00:00:00 2001 From: vizsatiz Date: Mon, 16 Dec 2024 23:33:01 +0530 Subject: [PATCH 1/4] Implemented the mechanism to use literals in output parser --- examples/python/output_parser.py | 5 +++-- flo_ai/parsers/flo_json_parser.py | 33 ++++++++++++++++++++++++------- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/examples/python/output_parser.py b/examples/python/output_parser.py index c07e7d0f..07cbda4a 100644 --- a/examples/python/output_parser.py +++ b/examples/python/output_parser.py @@ -34,9 +34,10 @@ 'name': 'middle_name', }, { - 'type': 'str', - 'description': 'The last name of the person', + 'type': 'literal', + 'description': 'The last name of the person, the value can be either of Vishnu or Satis', 'name': 'last_name', + 'values': ['Vishnu', 'Satis'], }, ], } diff --git a/flo_ai/parsers/flo_json_parser.py b/flo_ai/parsers/flo_json_parser.py index c1c11a7f..0733bbde 100644 --- a/flo_ai/parsers/flo_json_parser.py +++ b/flo_ai/parsers/flo_json_parser.py @@ -1,6 +1,6 @@ import json from flo_ai.parsers.flo_parser import FloParser -from typing import List, Dict, Any, Optional +from typing import List, Dict, Any, Optional, Literal from pydantic import BaseModel, Field, create_model from flo_ai.error.flo_exception import FloException from langchain_core.output_parsers import PydanticOutputParser @@ -19,14 +19,33 @@ def __init__(self, parse_contract: ParseContract): super().__init__() def __create_contract_from_json(self) -> BaseModel: - type_mapping = {'str': str, 'int': int, 'bool': bool, 'float': float} - pydantic_fields = { - field['name']: ( - type_mapping[field['type']], + type_mapping = { + 'str': str, + 'int': int, + 'bool': bool, + 'float': float, + 'literal': Literal, + } + pydantic_fields = {} + for field in self.contract.fields: + field_type = field['type'] + if field_type == 'literal': + literal_values = field.get('values', []) + if not literal_values: + raise ValueError( + f"Field '{field['name']}' of type 'literal' must specify 'values'." + ) + field_type_annotation = Literal[tuple(literal_values)] + else: + field_type_annotation = type_mapping.get(field_type) + if field_type_annotation is None: + raise ValueError(f'Unsupported type: {field_type}') + + pydantic_fields[field['name']] = ( + field_type_annotation, Field(..., description=field['description']), ) - for field in self.contract.fields - } + DynamicModel = create_model(self.contract.name, **pydantic_fields) return DynamicModel From ed8efdfc89596e71dc2d998a511bea32bb41c011 Mon Sep 17 00:00:00 2001 From: vizsatiz Date: Mon, 16 Dec 2024 23:50:55 +0530 Subject: [PATCH 2/4] feature to add default value prompt, and automatic prompt for literals --- examples/python/output_parser.py | 6 +++++- flo_ai/parsers/flo_json_parser.py | 35 +++++++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/examples/python/output_parser.py b/examples/python/output_parser.py index 07cbda4a..689717e9 100644 --- a/examples/python/output_parser.py +++ b/examples/python/output_parser.py @@ -37,7 +37,11 @@ 'type': 'literal', 'description': 'The last name of the person, the value can be either of Vishnu or Satis', 'name': 'last_name', - 'values': ['Vishnu', 'Satis'], + 'values': [ + {'value': 'Vishnu', 'description': 'If the first_name starts with K'}, + {'value': 'Satis', 'description': 'If the first_name starts with M'}, + ], + 'default_value_prompt': 'If none of the above value is suited, please use value other than the above in snake-case', }, ], } diff --git a/flo_ai/parsers/flo_json_parser.py b/flo_ai/parsers/flo_json_parser.py index 0733bbde..98ae72d7 100644 --- a/flo_ai/parsers/flo_json_parser.py +++ b/flo_ai/parsers/flo_json_parser.py @@ -1,4 +1,6 @@ import json +import csv +from io import StringIO from flo_ai.parsers.flo_parser import FloParser from typing import List, Dict, Any, Optional, Literal from pydantic import BaseModel, Field, create_model @@ -18,6 +20,21 @@ def __init__(self, parse_contract: ParseContract): self.contract = parse_contract super().__init__() + def __dict_list_to_csv_string(self, data): + if not data or len(data) == 0: + return '```No data provided```' + headers = data[0].keys() + output = StringIO() + + writer = csv.DictWriter(output, fieldnames=headers) + writer.writeheader() + writer.writerows(data) + + csv_string = output.getvalue() + output.close() + + return f'```\n{csv_string}```' + def __create_contract_from_json(self) -> BaseModel: type_mapping = { 'str': str, @@ -35,15 +52,29 @@ def __create_contract_from_json(self) -> BaseModel: raise ValueError( f"Field '{field['name']}' of type 'literal' must specify 'values'." ) - field_type_annotation = Literal[tuple(literal_values)] + literals = [literal_value['value'] for literal_value in literal_values] + field_type_annotation = Literal[tuple(literals)] + default_prompt = ( + field['default_value_prompt'] + if 'default_value_prompt' in field + else '' + ) + field_description = f""" + {field['description']} + Following are the list of possibles values and its correponding description: + {self.__dict_list_to_csv_string(literal_values)} + + {default_prompt} + """ else: field_type_annotation = type_mapping.get(field_type) if field_type_annotation is None: raise ValueError(f'Unsupported type: {field_type}') + field_description = field['description'] pydantic_fields[field['name']] = ( field_type_annotation, - Field(..., description=field['description']), + Field(..., description=field_description), ) DynamicModel = create_model(self.contract.name, **pydantic_fields) From 5ba539f3bfeae04de21d2895a6798a7771ef8c86 Mon Sep 17 00:00:00 2001 From: vizsatiz Date: Mon, 16 Dec 2024 23:53:03 +0530 Subject: [PATCH 3/4] Upgrading the version for dev3 release --- pyproject.toml | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index c6edd8b5..4e3a6c5e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "flo-ai" -version = "0.0.5-dev2" +version = "0.0.5-dev3" description = "A easy way to create structured AI agents" authors = ["vizsatiz "] license = "MIT" diff --git a/setup.py b/setup.py index f682b6f4..4f28b366 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name='flo-ai', - version='0.0.5-dev2', + version='0.0.5-dev3', author='Rootflo', description='Create composable AI agents', long_description=long_description, From a3917ca5e48c41de895f255a7271244b7969250d Mon Sep 17 00:00:00 2001 From: vizsatiz Date: Mon, 16 Dec 2024 23:56:06 +0530 Subject: [PATCH 4/4] Further improving the prompt --- .gitignore | 3 ++- flo_ai/parsers/flo_json_parser.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 25d51645..69cc4315 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,5 @@ bin .DS_Store *.sql *.log -*.yaml \ No newline at end of file +*.yaml +examples/local/* \ No newline at end of file diff --git a/flo_ai/parsers/flo_json_parser.py b/flo_ai/parsers/flo_json_parser.py index 98ae72d7..adf6db40 100644 --- a/flo_ai/parsers/flo_json_parser.py +++ b/flo_ai/parsers/flo_json_parser.py @@ -64,6 +64,7 @@ def __create_contract_from_json(self) -> BaseModel: Following are the list of possibles values and its correponding description: {self.__dict_list_to_csv_string(literal_values)} + This should be one of the values in the `value` column in the above csv. {default_prompt} """ else: