langroid 0.16.7__py3-none-any.whl → 0.17.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. langroid/agent/base.py +45 -21
  2. langroid/agent/chat_agent.py +22 -14
  3. langroid/agent/chat_document.py +22 -13
  4. langroid/agent/tool_message.py +11 -11
  5. langroid/agent/tools/file_tools.py +234 -0
  6. langroid/agent/xml_tool_message.py +179 -45
  7. langroid/utils/constants.py +2 -0
  8. langroid/utils/git_utils.py +251 -0
  9. langroid/utils/system.py +78 -0
  10. {langroid-0.16.7.dist-info → langroid-0.17.1.dist-info}/METADATA +6 -3
  11. {langroid-0.16.7.dist-info → langroid-0.17.1.dist-info}/RECORD +14 -89
  12. pyproject.toml +3 -2
  13. langroid/agent/md_tool_message_grammar.py +0 -455
  14. langroid/agent/tools/code_file_tool_parse.py +0 -150
  15. langroid/agent/tools/code_file_tool_pyparsing.py +0 -194
  16. langroid/agent/tools/code_file_tool_pyparsing2.py +0 -199
  17. langroid/agent/tools/extract_tool.py +0 -96
  18. langroid/agent/tools/formatted_model_custom.py +0 -150
  19. langroid/agent/tools/formatted_model_custom2.py +0 -168
  20. langroid/agent/tools/formatted_model_custom3.py +0 -279
  21. langroid/agent/tools/formatted_model_custom4.py +0 -395
  22. langroid/agent/tools/formatted_model_jinja.py +0 -133
  23. langroid/agent/tools/formatted_model_jinja.py-e +0 -122
  24. langroid/agent/tools/formatted_model_jinja2.py +0 -145
  25. langroid/agent/tools/formatted_model_jinja2.py-e +0 -135
  26. langroid/agent/tools/formatted_model_lark.py +0 -0
  27. langroid/agent/tools/formatted_model_lark2.py +0 -168
  28. langroid/agent/tools/formatted_model_parse.py +0 -105
  29. langroid/agent/tools/formatted_model_parse.py-e +0 -98
  30. langroid/agent/tools/formatted_model_parse2.py +0 -113
  31. langroid/agent/tools/formatted_model_parse2.py-e +0 -109
  32. langroid/agent/tools/formatted_model_parse3.py +0 -114
  33. langroid/agent/tools/formatted_model_parse3.py-e +0 -110
  34. langroid/agent/tools/formatted_model_parsimon.py +0 -194
  35. langroid/agent/tools/formatted_model_parsimon.py-e +0 -186
  36. langroid/agent/tools/formatted_model_pyparsing.py +0 -169
  37. langroid/agent/tools/formatted_model_pyparsing.py-e +0 -149
  38. langroid/agent/tools/formatted_model_pyparsing2.py +0 -159
  39. langroid/agent/tools/formatted_model_pyparsing2.py-e +0 -143
  40. langroid/agent/tools/formatted_model_pyparsing3.py +0 -133
  41. langroid/agent/tools/formatted_model_pyparsing3.py-e +0 -121
  42. langroid/agent/tools/formatted_model_pyparsing4.py +0 -213
  43. langroid/agent/tools/formatted_model_pyparsing4.py-e +0 -176
  44. langroid/agent/tools/formatted_model_pyparsing5.py +0 -173
  45. langroid/agent/tools/formatted_model_pyparsing5.py-e +0 -142
  46. langroid/agent/tools/formatted_model_regex.py +0 -246
  47. langroid/agent/tools/formatted_model_regex.py-e +0 -248
  48. langroid/agent/tools/formatted_model_regex2.py +0 -250
  49. langroid/agent/tools/formatted_model_regex2.py-e +0 -253
  50. langroid/agent/tools/formatted_model_tatsu.py +0 -172
  51. langroid/agent/tools/formatted_model_tatsu.py-e +0 -160
  52. langroid/agent/tools/formatted_model_template.py +0 -217
  53. langroid/agent/tools/formatted_model_template.py-e +0 -200
  54. langroid/agent/tools/formatted_model_xml.py +0 -178
  55. langroid/agent/tools/formatted_model_xml2.py +0 -178
  56. langroid/agent/tools/formatted_model_xml3.py +0 -132
  57. langroid/agent/tools/formatted_model_xml4.py +0 -130
  58. langroid/agent/tools/formatted_model_xml5.py +0 -130
  59. langroid/agent/tools/formatted_model_xml6.py +0 -113
  60. langroid/agent/tools/formatted_model_xml7.py +0 -117
  61. langroid/agent/tools/formatted_model_xml8.py +0 -164
  62. langroid/agent/tools/generator_tool.py +0 -20
  63. langroid/agent/tools/generic_tool.py +0 -165
  64. langroid/agent/tools/generic_tool_tatsu.py +0 -275
  65. langroid/agent/tools/grammar_based_model.py +0 -132
  66. langroid/agent/tools/grammar_based_model.py-e +0 -128
  67. langroid/agent/tools/grammar_based_model_lark.py +0 -156
  68. langroid/agent/tools/grammar_based_model_lark.py-e +0 -153
  69. langroid/agent/tools/grammar_based_model_parse.py +0 -86
  70. langroid/agent/tools/grammar_based_model_parse.py-e +0 -80
  71. langroid/agent/tools/grammar_based_model_parsimonious.py +0 -129
  72. langroid/agent/tools/grammar_based_model_parsimonious.py-e +0 -120
  73. langroid/agent/tools/grammar_based_model_pyparsing.py +0 -105
  74. langroid/agent/tools/grammar_based_model_pyparsing.py-e +0 -103
  75. langroid/agent/tools/grammar_based_model_regex.py +0 -139
  76. langroid/agent/tools/grammar_based_model_regex.py-e +0 -130
  77. langroid/agent/tools/grammar_based_model_regex2.py +0 -124
  78. langroid/agent/tools/grammar_based_model_regex2.py-e +0 -116
  79. langroid/agent/tools/grammar_based_model_tatsu.py +0 -80
  80. langroid/agent/tools/grammar_based_model_tatsu.py-e +0 -77
  81. langroid/agent/tools/lark_earley_example.py +0 -135
  82. langroid/agent/tools/lark_earley_example.py-e +0 -117
  83. langroid/agent/tools/lark_example.py +0 -72
  84. langroid/agent/tools/note_tool.py +0 -0
  85. langroid/agent/tools/parse_example.py +0 -76
  86. langroid/agent/tools/parse_example2.py +0 -87
  87. langroid/agent/tools/parse_example3.py +0 -42
  88. langroid/agent/tools/parse_test.py +0 -791
  89. langroid/agent/tools/run_python_code.py +0 -60
  90. {langroid-0.16.7.dist-info → langroid-0.17.1.dist-info}/LICENSE +0 -0
  91. {langroid-0.16.7.dist-info → langroid-0.17.1.dist-info}/WHEEL +0 -0
@@ -1,120 +0,0 @@
1
- from typing import Dict, ClassVar
2
- from pydantic import BaseModel
3
- from parsimonious import Grammar, NodeVisitor
4
- from abc import ABC, abstractmethod
5
-
6
- class GrammarBasedModel(BaseModel, ABC):
7
- grammar: ClassVar[str]
8
- start_token: ClassVar[str]
9
- end_token: ClassVar[str]
10
- field_mappings: ClassVar[Dict[str, str]]
11
-
12
- @classmethod
13
- @abstractmethod
14
- def get_grammar(cls) -> str:
15
- pass
16
-
17
- @classmethod
18
- def parse(cls, text: str) -> 'GrammarBasedModel':
19
- grammar = Grammar(cls.get_grammar())
20
- tree = grammar.parse(text[len(cls.start_token):-len(cls.end_token)].strip())
21
-
22
- class ModelVisitor(NodeVisitor):
23
- def __init__(self, field_mappings):
24
- self.field_mappings = field_mappings
25
- self.data = {}
26
-
27
- def generic_visit(self, node, visited_children):
28
- return visited_children or node.text
29
-
30
- def visit_start(self, node, visited_children):
31
- return self.data
32
-
33
- def __getattr__(self, name):
34
- if name.startswith('visit_'):
35
- field = name[6:]
36
- if field in self.field_mappings.values():
37
- def visit_method(node, visited_children):
38
- model_field = next(k for k, v in self.field_mappings.items() if v == field)
39
- self.data[model_field] = node.text.strip()
40
- return node.text
41
- return visit_method
42
- return super().__getattribute__(name)
43
-
44
- visitor = ModelVisitor(cls.field_mappings)
45
- model_dict = visitor.visit(tree)
46
- return cls(**model_dict)
47
-
48
- def generate(self) -> str:
49
- grammar = Grammar(self.get_grammar())
50
-
51
- class ModelGenerator(NodeVisitor):
52
- def __init__(self, model):
53
- self.model = model
54
-
55
- def generic_visit(self, node, visited_children):
56
- return ''.join(filter(None, visited_children))
57
-
58
- def __getattr__(self, name):
59
- if name.startswith('visit_'):
60
- field = name[6:]
61
- if field in self.model.field_mappings.values():
62
- model_field = next(k for k, v in self.model.field_mappings.items() if v == field)
63
- return lambda node, children: str(getattr(self.model, model_field))
64
- return lambda node, children: node.text
65
-
66
- generator = ModelGenerator(self)
67
- generated_content = generator.visit(grammar['start'])
68
- return f"{self.start_token}\n{generated_content}\n{self.end_token}"
69
-
70
- class PersonSpec(GrammarBasedModel):
71
- name: str
72
- age: int
73
- city: str
74
-
75
- grammar = r"""
76
- start = name_line age_line city_line
77
- name_line = "name:" ws name newline
78
- age_line = "age is" ws age newline
79
- city_line = "lives in" ws city newline?
80
- name = ~r"[^\n]+"
81
- age = ~r"\d+"
82
- city = ~r"[^\n]+"
83
- ws = ~r"\s+"
84
- newline = ~r"\n"
85
- """
86
- start_token = "<spec>"
87
- end_token = "</spec>"
88
- field_mappings = {
89
- "name": "name",
90
- "age": "age",
91
- "city": "city"
92
- }
93
-
94
- @classmethod
95
- def get_grammar(cls):
96
- return cls.grammar
97
-
98
- if __name__ == "__main__":
99
- # Test parsing
100
- input_str = """<spec>
101
- name: John Doe
102
- age is 30
103
- lives in New York
104
- </spec>"""
105
- person = PersonSpec.parse(input_str)
106
- print("Parsed person:", person)
107
-
108
- # Test generation
109
- generated_str = person.generate()
110
- print("\nGenerated string:")
111
- print(generated_str)
112
-
113
- # Test round-trip
114
- round_trip_person = PersonSpec.parse(generated_str)
115
- print("\nRound-trip parsed person:", round_trip_person)
116
-
117
- assert person == round_trip_person, "Round-trip parsing failed"
118
- print("\nRound-trip test passed!")
119
-
120
-
@@ -1,105 +0,0 @@
1
- from abc import ABC, abstractmethod
2
- from typing import Dict
3
-
4
- from pyparsing import *
5
-
6
- from langroid.pydantic_v1 import BaseModel
7
-
8
-
9
- class GrammarBasedModel(BaseModel, ABC):
10
- @classmethod
11
- @abstractmethod
12
- def grammar(cls) -> ParserElement:
13
- pass
14
-
15
- @classmethod
16
- @abstractmethod
17
- def start_token(cls) -> str:
18
- pass
19
-
20
- @classmethod
21
- @abstractmethod
22
- def end_token(cls) -> str:
23
- pass
24
-
25
- @classmethod
26
- @abstractmethod
27
- def field_mappings(cls) -> Dict[str, str]:
28
- pass
29
-
30
- @classmethod
31
- def parse(cls, text: str) -> "GrammarBasedModel":
32
- full_grammar = (
33
- Suppress(cls.start_token()) + cls.grammar() + Suppress(cls.end_token())
34
- )
35
- parsed = full_grammar.parseString(text, parseAll=True)
36
- return cls(
37
- **{
38
- field: parsed[token][-1]
39
- for field, token in cls.field_mappings().items()
40
- }
41
- )
42
-
43
- def generate(self) -> str:
44
- result = [self.start_token()]
45
- for field, token in self.field_mappings().items():
46
- value = getattr(self, field)
47
- if token == "name":
48
- result.append(f"name: {value}")
49
- elif token == "age":
50
- result.append(f"age is {value}")
51
- elif token == "city":
52
- result.append(f"lives in {value}")
53
- result.append(self.end_token())
54
- return "\n".join(result)
55
-
56
-
57
- class PersonSpec(GrammarBasedModel):
58
- name: str
59
- age: int
60
- city: str
61
-
62
- @classmethod
63
- def grammar(cls):
64
- name = Group(Literal("name:") + Word(alphas))("name")
65
- age = Group(Literal("age is") + Word(nums))("age")
66
- city = Group(Literal("lives in") + Word(alphas))("city")
67
- return name + age + city
68
-
69
- @classmethod
70
- def start_token(cls):
71
- return "<spec>"
72
-
73
- @classmethod
74
- def end_token(cls):
75
- return "</spec>"
76
-
77
- @classmethod
78
- def field_mappings(cls):
79
- return {"name": "name", "age": "age", "city": "city"}
80
-
81
-
82
- if __name__ == "__main__":
83
- # Test parsing
84
- test_string = """
85
- <spec>
86
- name: John
87
- age is 30
88
- lives in Tokyo
89
- </spec>
90
- """
91
- parsed_person = PersonSpec.parse(test_string)
92
- print("Parsed person:", parsed_person)
93
-
94
- # Test generating
95
- new_person = PersonSpec(name="Alice", age=25, city="NewYork")
96
- generated_string = new_person.generate()
97
- print("\nGenerated string:")
98
- print(generated_string)
99
-
100
- # Test round-trip
101
- round_trip_person = PersonSpec.parse(generated_string)
102
- print("\nRound-trip parsed person:", round_trip_person)
103
-
104
- assert new_person == round_trip_person, "Round-trip parsing failed"
105
- print("\nRound-trip test passed!")
@@ -1,103 +0,0 @@
1
- from pydantic import BaseModel
2
- from pyparsing import *
3
- from abc import ABC, abstractmethod
4
- from typing import Dict, Any
5
-
6
- class GrammarBasedModel(BaseModel, ABC):
7
- @classmethod
8
- @abstractmethod
9
- def grammar(cls) -> ParserElement:
10
- pass
11
-
12
- @classmethod
13
- @abstractmethod
14
- def start_token(cls) -> str:
15
- pass
16
-
17
- @classmethod
18
- @abstractmethod
19
- def end_token(cls) -> str:
20
- pass
21
-
22
- @classmethod
23
- @abstractmethod
24
- def field_mappings(cls) -> Dict[str, str]:
25
- pass
26
-
27
- @classmethod
28
- def parse(cls, text: str) -> 'GrammarBasedModel':
29
- full_grammar = (
30
- Suppress(cls.start_token()) +
31
- cls.grammar() +
32
- Suppress(cls.end_token())
33
- )
34
- parsed = full_grammar.parseString(text, parseAll=True)
35
- return cls(**{field: parsed[token][-1] for field, token in cls.field_mappings().items()})
36
-
37
- def generate(self) -> str:
38
- result = [self.start_token()]
39
- for field, token in self.field_mappings().items():
40
- value = getattr(self, field)
41
- if token == "name":
42
- result.append(f"name: {value}")
43
- elif token == "age":
44
- result.append(f"age is {value}")
45
- elif token == "city":
46
- result.append(f"lives in {value}")
47
- result.append(self.end_token())
48
- return "\n".join(result)
49
-
50
- class PersonSpec(GrammarBasedModel):
51
- name: str
52
- age: int
53
- city: str
54
-
55
- @classmethod
56
- def grammar(cls):
57
- name = Group(Literal("name:") + Word(alphas))("name")
58
- age = Group(Literal("age is") + Word(nums))("age")
59
- city = Group(Literal("lives in") + Word(alphas))("city")
60
- return name + age + city
61
-
62
- @classmethod
63
- def start_token(cls):
64
- return "<spec>"
65
-
66
- @classmethod
67
- def end_token(cls):
68
- return "</spec>"
69
-
70
- @classmethod
71
- def field_mappings(cls):
72
- return {
73
- "name": "name",
74
- "age": "age",
75
- "city": "city"
76
- }
77
-
78
- if __name__ == "__main__":
79
- # Test parsing
80
- test_string = """
81
- <spec>
82
- name: John
83
- age is 30
84
- lives in Tokyo
85
- </spec>
86
- """
87
- parsed_person = PersonSpec.parse(test_string)
88
- print("Parsed person:", parsed_person)
89
-
90
- # Test generating
91
- new_person = PersonSpec(name="Alice", age=25, city="NewYork")
92
- generated_string = new_person.generate()
93
- print("\nGenerated string:")
94
- print(generated_string)
95
-
96
- # Test round-trip
97
- round_trip_person = PersonSpec.parse(generated_string)
98
- print("\nRound-trip parsed person:", round_trip_person)
99
-
100
- assert new_person == round_trip_person, "Round-trip parsing failed"
101
- print("\nRound-trip test passed!")
102
-
103
-
@@ -1,139 +0,0 @@
1
- import re
2
- from abc import ABC, abstractmethod
3
- from typing import Dict
4
-
5
- from langroid.pydantic_v1 import BaseModel
6
-
7
-
8
- class FormattingModel(BaseModel, ABC):
9
- @classmethod
10
- @abstractmethod
11
- def format_spec(cls) -> str:
12
- pass
13
-
14
- @classmethod
15
- @abstractmethod
16
- def start_token(cls) -> str:
17
- pass
18
-
19
- @classmethod
20
- @abstractmethod
21
- def end_token(cls) -> str:
22
- pass
23
-
24
- @classmethod
25
- @abstractmethod
26
- def field_mappings(cls) -> Dict[str, str]:
27
- pass
28
-
29
- @classmethod
30
- def _create_regex_pattern(cls) -> str:
31
- spec = cls.format_spec()
32
- for field, placeholder in cls.field_mappings().items():
33
- spec = spec.replace(placeholder, f"(?P<{field}>.*?)")
34
- pattern = (
35
- f"{re.escape(cls.start_token())}\\s*{spec}\\s*{re.escape(cls.end_token())}"
36
- )
37
- return pattern
38
-
39
- @classmethod
40
- def parse(cls, text: str) -> "FormattingModel":
41
- pattern = cls._create_regex_pattern()
42
- match = re.search(pattern, text, re.DOTALL | re.IGNORECASE)
43
- if match:
44
- return cls(**{k: v.strip() for k, v in match.groupdict().items()})
45
- raise ValueError(
46
- f"Text does not match the expected format. Pattern: {pattern}, Text: {text}"
47
- )
48
-
49
- def generate(self) -> str:
50
- content = self.format_spec()
51
- # Remove \s* patterns
52
- content = re.sub(r"\\s\*", " ", content)
53
- for field, placeholder in self.field_mappings().items():
54
- content = content.replace(placeholder, str(getattr(self, field)))
55
- return f"{self.start_token()}\n{content}\n{self.end_token()}"
56
-
57
-
58
- class PersonModel(FormattingModel):
59
- name: str
60
- age: int
61
- city: str
62
-
63
- @classmethod
64
- def format_spec(cls) -> str:
65
- return "name:\\s*{name}\\s*age is\\s*{age}\\s*lives in\\s*{city}"
66
-
67
- @classmethod
68
- def start_token(cls) -> str:
69
- return "<spec>"
70
-
71
- @classmethod
72
- def end_token(cls) -> str:
73
- return "</spec>"
74
-
75
- @classmethod
76
- def field_mappings(cls) -> Dict[str, str]:
77
- return {"name": "{name}", "age": "{age}", "city": "{city}"}
78
-
79
-
80
- def test_round_trip(model_class, input_string):
81
- # Parse the input string
82
- parsed_model = model_class.parse(input_string)
83
- print(f"Parsed model: {parsed_model}")
84
-
85
- # Generate a string from the parsed model
86
- generated_string = parsed_model.generate()
87
- print(f"Generated string:\n{generated_string}")
88
-
89
- # Parse the generated string
90
- reparsed_model = model_class.parse(generated_string)
91
- print(f"Reparsed model: {reparsed_model}")
92
-
93
- # Assert that the original parsed model and the reparsed model are equal
94
- assert (
95
- parsed_model == reparsed_model
96
- ), "Round trip failed: original and reparsed models are not equal"
97
-
98
- # Assert that all fields are present and have the correct types
99
- for field, field_type in model_class.__annotations__.items():
100
- assert hasattr(parsed_model, field), f"Field {field} is missing"
101
- assert isinstance(
102
- getattr(parsed_model, field), field_type
103
- ), f"Field {field} has incorrect type"
104
-
105
- print("Round trip test passed successfully!")
106
-
107
-
108
- if __name__ == "__main__":
109
- # Test case 1: Standard formatting
110
- test_string1 = """
111
- <spec>
112
- name: John Doe
113
- age is 30
114
- lives in New York
115
- </spec>
116
- """
117
- test_round_trip(PersonModel, test_string1)
118
-
119
- print("\n" + "=" * 50 + "\n")
120
-
121
- # Test case 2: Varying whitespace
122
- test_string2 = "<spec>name: Alice \nage is 25 \nlives in Tokyo</spec>"
123
- test_round_trip(PersonModel, test_string2)
124
-
125
- print("\n" + "=" * 50 + "\n")
126
-
127
- # Test case 3: Multiline values
128
- test_string3 = """
129
- <spec>
130
- name: Bob
131
- Smith
132
- age is 40
133
- lives in San
134
- Francisco
135
- </spec>
136
- """
137
- test_round_trip(PersonModel, test_string3)
138
-
139
- print("All tests passed successfully!")
@@ -1,130 +0,0 @@
1
- import re
2
- from abc import ABC, abstractmethod
3
- from typing import Dict, Any
4
- from pydantic import BaseModel
5
-
6
- class FormattingModel(BaseModel, ABC):
7
- @classmethod
8
- @abstractmethod
9
- def format_spec(cls) -> str:
10
- pass
11
-
12
- @classmethod
13
- @abstractmethod
14
- def start_token(cls) -> str:
15
- pass
16
-
17
- @classmethod
18
- @abstractmethod
19
- def end_token(cls) -> str:
20
- pass
21
-
22
- @classmethod
23
- @abstractmethod
24
- def field_mappings(cls) -> Dict[str, str]:
25
- pass
26
-
27
- @classmethod
28
- def _create_regex_pattern(cls) -> str:
29
- spec = cls.format_spec()
30
- for field, placeholder in cls.field_mappings().items():
31
- spec = spec.replace(placeholder, f"(?P<{field}>.*?)")
32
- pattern = f"{re.escape(cls.start_token())}\\s*{spec}\\s*{re.escape(cls.end_token())}"
33
- return pattern
34
-
35
- @classmethod
36
- def parse(cls, text: str) -> 'FormattingModel':
37
- pattern = cls._create_regex_pattern()
38
- match = re.search(pattern, text, re.DOTALL | re.IGNORECASE)
39
- if match:
40
- return cls(**{k: v.strip() for k, v in match.groupdict().items()})
41
- raise ValueError(f"Text does not match the expected format. Pattern: {pattern}, Text: {text}")
42
-
43
- def generate(self) -> str:
44
- content = self.format_spec()
45
- # Remove \s* patterns
46
- content = re.sub(r'\\s\*', ' ', content)
47
- for field, placeholder in self.field_mappings().items():
48
- content = content.replace(placeholder, str(getattr(self, field)))
49
- return f"{self.start_token()}\n{content}\n{self.end_token()}"
50
-
51
- class PersonModel(FormattingModel):
52
- name: str
53
- age: int
54
- city: str
55
-
56
- @classmethod
57
- def format_spec(cls) -> str:
58
- return "name:\\s*{name}\\s*age is\\s*{age}\\s*lives in\\s*{city}"
59
-
60
- @classmethod
61
- def start_token(cls) -> str:
62
- return "<spec>"
63
-
64
- @classmethod
65
- def end_token(cls) -> str:
66
- return "</spec>"
67
-
68
- @classmethod
69
- def field_mappings(cls) -> Dict[str, str]:
70
- return {
71
- "name": "{name}",
72
- "age": "{age}",
73
- "city": "{city}"
74
- }
75
-
76
- def test_round_trip(model_class, input_string):
77
- # Parse the input string
78
- parsed_model = model_class.parse(input_string)
79
- print(f"Parsed model: {parsed_model}")
80
-
81
- # Generate a string from the parsed model
82
- generated_string = parsed_model.generate()
83
- print(f"Generated string:\n{generated_string}")
84
-
85
- # Parse the generated string
86
- reparsed_model = model_class.parse(generated_string)
87
- print(f"Reparsed model: {reparsed_model}")
88
-
89
- # Assert that the original parsed model and the reparsed model are equal
90
- assert parsed_model == reparsed_model, "Round trip failed: original and reparsed models are not equal"
91
-
92
- # Assert that all fields are present and have the correct types
93
- for field, field_type in model_class.__annotations__.items():
94
- assert hasattr(parsed_model, field), f"Field {field} is missing"
95
- assert isinstance(getattr(parsed_model, field), field_type), f"Field {field} has incorrect type"
96
-
97
- print("Round trip test passed successfully!")
98
-
99
- if __name__ == "__main__":
100
- # Test case 1: Standard formatting
101
- test_string1 = """
102
- <spec>
103
- name: John Doe
104
- age is 30
105
- lives in New York
106
- </spec>
107
- """
108
- test_round_trip(PersonModel, test_string1)
109
-
110
- print("\n" + "="*50 + "\n")
111
-
112
- # Test case 2: Varying whitespace
113
- test_string2 = "<spec>name: Alice \nage is 25 \nlives in Tokyo</spec>"
114
- test_round_trip(PersonModel, test_string2)
115
-
116
- print("\n" + "="*50 + "\n")
117
-
118
- # Test case 3: Multiline values
119
- test_string3 = """
120
- <spec>
121
- name: Bob
122
- Smith
123
- age is 40
124
- lives in San
125
- Francisco
126
- </spec>
127
- """
128
- test_round_trip(PersonModel, test_string3)
129
-
130
- print("All tests passed successfully!")