langroid 0.16.7__py3-none-any.whl → 0.17.0__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 +178 -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.0.dist-info}/METADATA +6 -3
  11. {langroid-0.16.7.dist-info → langroid-0.17.0.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.0.dist-info}/LICENSE +0 -0
  91. {langroid-0.16.7.dist-info → langroid-0.17.0.dist-info}/WHEEL +0 -0
@@ -1,186 +0,0 @@
1
- from abc import ABC, abstractmethod
2
- from pydantic import BaseModel
3
- from parsimonious import Grammar, NodeVisitor, exceptions
4
-
5
- class FormattingModel(BaseModel, ABC):
6
- @classmethod
7
- @abstractmethod
8
- def format_spec(cls):
9
- pass
10
-
11
- @classmethod
12
- @abstractmethod
13
- def parse_spec(cls):
14
- pass
15
-
16
- @classmethod
17
- @abstractmethod
18
- def parse_visitor(cls):
19
- pass
20
-
21
- @classmethod
22
- @abstractmethod
23
- def start_token(cls) -> str:
24
- pass
25
-
26
- @classmethod
27
- @abstractmethod
28
- def end_token(cls) -> str:
29
- pass
30
-
31
- @classmethod
32
- def format(cls, instance: 'FormattingModel') -> str:
33
- spec = cls.format_spec()
34
- formatted = spec.format(**instance.dict())
35
- return f"{cls.start_token()}\n{formatted}\n{cls.end_token()}"
36
-
37
- @classmethod
38
- def parse(cls, formatted_string: str) -> 'FormattingModel':
39
- lines = formatted_string.strip().split('\n')
40
- if lines[0] != cls.start_token() or lines[-1] != cls.end_token():
41
- raise ValueError("Invalid start or end token")
42
- content = '\n'.join(lines[1:-1])
43
-
44
- grammar = Grammar(cls.parse_spec())
45
- try:
46
- tree = grammar.parse(content)
47
- except exceptions.ParseError as e:
48
- raise ValueError(f"Failed to parse content: {e}\nContent:\n{content}")
49
-
50
- visitor = cls.parse_visitor()()
51
- try:
52
- visitor_result = visitor.visit(tree)
53
- except Exception as e:
54
- raise ValueError(f"Error during tree visitation: {e}")
55
-
56
- if not all(field in visitor_result for field in cls.__fields__):
57
- missing_fields = [field for field in cls.__fields__ if field not in visitor_result]
58
- raise ValueError(f"Missing fields after parsing: {', '.join(missing_fields)}")
59
-
60
- return cls(**visitor_result)
61
-
62
-
63
- from parsimonious import Grammar, NodeVisitor
64
-
65
- class CodeFileModel(FormattingModel):
66
- language: str
67
- file_path: str
68
- code: str
69
-
70
- @classmethod
71
- def format_spec(cls):
72
- return "code_file_model\n{file_path}\n```{language}\n{code}\n```"
73
-
74
- @classmethod
75
- def parse_spec(cls):
76
- return """
77
- model = "code_file_model" newline file_path newline code_block
78
- file_path = line
79
- code_block = "```" language newline code "```"
80
- language = ~"[^\\n]+"
81
- code = (!(~"```") (newline / any_char))*
82
- line = ~"[^\\n]+"
83
- newline = ~"\\s*\\n\\s*"
84
- any_char = ~"."
85
- """
86
-
87
- @classmethod
88
- def parse_visitor(cls):
89
- class Visitor(NodeVisitor):
90
- def __init__(self):
91
- self.data = {}
92
-
93
- def visit_model(self, node, visited_children):
94
- return self.data
95
-
96
- def visit_file_path(self, node, visited_children):
97
- self.data['file_path'] = node.text.strip()
98
-
99
- def visit_language(self, node, visited_children):
100
- self.data['language'] = node.text.strip()
101
-
102
- def visit_code(self, node, visited_children):
103
- self.data['code'] = node.text.strip()
104
-
105
- def generic_visit(self, node, visited_children):
106
- return visited_children or node
107
-
108
- return Visitor
109
-
110
- @classmethod
111
- def start_token(cls):
112
- return '<format>'
113
-
114
- @classmethod
115
- def end_token(cls):
116
- return '</format>'
117
-
118
-
119
-
120
-
121
-
122
-
123
-
124
-
125
- # Test cases
126
- if __name__ == "__main__":
127
- # Test formatting
128
- code_file = CodeFileModel(
129
- language="Python",
130
- file_path="src/main.py",
131
- code="def hello():\n print('Hello, World!')"
132
- )
133
- formatted = CodeFileModel.format(code_file)
134
- expected_format = """<format>
135
- code_file_model
136
- src/main.py
137
- ```Python
138
- def hello():
139
- print('Hello, World!')
140
- ```
141
- </format>"""
142
- assert formatted == expected_format, f"Formatting failed. Expected:\n{expected_format}\nGot:\n{formatted}"
143
- print("Formatting test passed.")
144
-
145
- # Test parsing
146
- parsed = CodeFileModel.parse(formatted)
147
- assert parsed == code_file, f"Parsing failed. Expected:\n{code_file}\nGot:\n{parsed}"
148
- print("Parsing test passed.")
149
-
150
- # Test round-trip
151
- round_trip = CodeFileModel.parse(CodeFileModel.format(code_file))
152
- assert round_trip == code_file, f"Round-trip failed. Expected:\n{code_file}\nGot:\n{round_trip}"
153
- print("Round-trip test passed.")
154
-
155
- # Test with different values
156
- code_file2 = CodeFileModel(
157
- language="JavaScript",
158
- file_path="src/app.js",
159
- code="function greet() {\n console.log('Hello, World!');\n}"
160
- )
161
- formatted2 = CodeFileModel.format(code_file2)
162
- parsed2 = CodeFileModel.parse(formatted2)
163
- assert parsed2 == code_file2, f"Parsing failed for different values. Expected:\n{code_file2}\nGot:\n{parsed2}"
164
- print("Different values test passed.")
165
-
166
- # Test tolerant parsing
167
- tolerant_input = """<format>
168
- code_file_model
169
- src/main.py
170
-
171
- ``` Python
172
- def hello():
173
- print('Hello, World!')
174
- ```
175
- </format>"""
176
- parsed_tolerant = CodeFileModel.parse(tolerant_input)
177
- expected_tolerant = CodeFileModel(
178
- language="Python",
179
- file_path="src/main.py",
180
- code="def hello():\n print('Hello, World!')"
181
- )
182
- assert parsed_tolerant == expected_tolerant, f"Tolerant parsing failed. Expected:\n{expected_tolerant}\nGot:\n{parsed_tolerant}"
183
- print("Tolerant parsing test passed.")
184
-
185
- print("All tests passed successfully!")
186
-
@@ -1,169 +0,0 @@
1
- from abc import ABC, abstractmethod
2
- from typing import Dict, Type, TypeVar
3
-
4
- from pyparsing import (
5
- LineEnd,
6
- ParseException,
7
- ParserElement,
8
- Regex,
9
- SkipTo,
10
- Suppress,
11
- Word,
12
- alphanums,
13
- )
14
-
15
- from langroid.pydantic_v1 import BaseModel
16
-
17
- T = TypeVar("T", bound="FormattingModel")
18
-
19
-
20
- class FormattingModel(BaseModel, ABC):
21
- @classmethod
22
- @abstractmethod
23
- def format_spec(cls) -> tuple[str, ParserElement]:
24
- pass
25
-
26
- @classmethod
27
- @abstractmethod
28
- def start_token(cls) -> str:
29
- pass
30
-
31
- @classmethod
32
- @abstractmethod
33
- def end_token(cls) -> str:
34
- pass
35
-
36
- @classmethod
37
- @abstractmethod
38
- def field_mappings(cls) -> Dict[str, str]:
39
- pass
40
-
41
- @classmethod
42
- def parse(cls: Type[T], text: str) -> T:
43
- content = text.strip()[len(cls.start_token()) : -len(cls.end_token())].strip()
44
- try:
45
- _, parser = cls.format_spec()
46
- parsed = parser.parseString(content)
47
- data = {
48
- field: parsed[token].strip()
49
- for field, token in cls.field_mappings().items()
50
- }
51
- return cls(**data)
52
- except ParseException as e:
53
- print(f"Parsing error: {e}")
54
- raise
55
-
56
- def generate(self) -> str:
57
- template, _ = self.format_spec()
58
- for field, token in self.field_mappings().items():
59
- value = getattr(self, field)
60
- template = template.replace(f"{{{token}}}", str(value))
61
- return f"{self.start_token()}\n{template}\n{self.end_token()}"
62
-
63
-
64
- class MyFormattedModel(FormattingModel):
65
- name: str
66
- age: int
67
- city: str
68
-
69
- @classmethod
70
- def format_spec(cls) -> tuple[str, ParserElement]:
71
- template = "name: {NAME}\n{AGE} is the age\nlives in {CITY}"
72
- name = Suppress("name:") + Word(alphanums + " ")("NAME") + LineEnd()
73
- age = Word(alphanums)("AGE") + Suppress("is the age") + LineEnd()
74
- city = Suppress("lives in") + SkipTo(LineEnd())("CITY")
75
- parser = name + age + city
76
- return template, parser
77
-
78
- @classmethod
79
- def start_token(cls) -> str:
80
- return "<format>"
81
-
82
- @classmethod
83
- def end_token(cls) -> str:
84
- return "</format>"
85
-
86
- @classmethod
87
- def field_mappings(cls) -> Dict[str, str]:
88
- return {"name": "NAME", "age": "AGE", "city": "CITY"}
89
-
90
-
91
- class CodeFileModel(FormattingModel):
92
- language: str
93
- file_path: str
94
- code: str
95
-
96
- @classmethod
97
- def format_spec(cls) -> tuple[str, ParserElement]:
98
- template = "code_file_model\nfile_path: {FILE_PATH}\n```{LANGUAGE}\n{CODE}\n```"
99
- file_path = (
100
- Suppress("code_file_model")
101
- + LineEnd()
102
- + Suppress("file_path:")
103
- + SkipTo(LineEnd())("FILE_PATH")
104
- + LineEnd()
105
- )
106
- code_block = (
107
- Suppress("```")
108
- + Word(alphanums)("LANGUAGE")
109
- + LineEnd()
110
- + Regex(r"(?s).*?(?=```)").set_parse_action(lambda s, l, t: t[0].strip())(
111
- "CODE"
112
- )
113
- + Suppress("```")
114
- )
115
- parser = file_path + code_block
116
- return template, parser
117
-
118
- @classmethod
119
- def start_token(cls) -> str:
120
- return "<format>"
121
-
122
- @classmethod
123
- def end_token(cls) -> str:
124
- return "</format>"
125
-
126
- @classmethod
127
- def field_mappings(cls) -> Dict[str, str]:
128
- return {"file_path": "FILE_PATH", "language": "LANGUAGE", "code": "CODE"}
129
-
130
-
131
- if __name__ == "__main__":
132
- # Test MyFormattedModel
133
- model = MyFormattedModel(name="John", age=30, city="Tokyo")
134
- generated = model.generate()
135
- print("Generated MyFormattedModel string:")
136
- print(generated)
137
- print()
138
-
139
- parsed = MyFormattedModel.parse(generated)
140
- print("Parsed MyFormattedModel object:")
141
- print(parsed)
142
- print()
143
-
144
- print("MyFormattedModel Round-trip test:")
145
- assert model == parsed, "MyFormattedModel: Original != Parsed"
146
- print("Passed!")
147
- print()
148
-
149
- # Test CodeFileModel
150
- code_model = CodeFileModel(
151
- language="python",
152
- file_path="src/main.py",
153
- code='def hello():\n print("Hello, World!")',
154
- )
155
- code_generated = code_model.generate()
156
- print("Generated CodeFileModel string:")
157
- print(code_generated)
158
- print()
159
-
160
- code_parsed = CodeFileModel.parse(code_generated)
161
- print("Parsed CodeFileModel object:")
162
- print(code_parsed)
163
- print()
164
-
165
- print("CodeFileModel Round-trip test:")
166
- assert code_model == code_parsed, "CodeFileModel: Original != Parsed"
167
- print("Passed!")
168
-
169
- print("\nAll tests passed successfully!")
@@ -1,149 +0,0 @@
1
- from pydantic import BaseModel
2
- from abc import ABC, abstractmethod
3
- from typing import Dict, Type, TypeVar
4
- from pyparsing import (
5
- Word, alphanums, Suppress, SkipTo, LineEnd,
6
- QuotedString, delimitedList, Group, OneOrMore, ParseException,
7
- ParserElement, Regex
8
- )
9
-
10
- T = TypeVar('T', bound='FormattingModel')
11
-
12
- class FormattingModel(BaseModel, ABC):
13
- @classmethod
14
- @abstractmethod
15
- def format_spec(cls) -> tuple[str, ParserElement]:
16
- pass
17
-
18
- @classmethod
19
- @abstractmethod
20
- def start_token(cls) -> str:
21
- pass
22
-
23
- @classmethod
24
- @abstractmethod
25
- def end_token(cls) -> str:
26
- pass
27
-
28
- @classmethod
29
- @abstractmethod
30
- def field_mappings(cls) -> Dict[str, str]:
31
- pass
32
-
33
- @classmethod
34
- def parse(cls: Type[T], text: str) -> T:
35
- content = text.strip()[len(cls.start_token()):-len(cls.end_token())].strip()
36
- try:
37
- _, parser = cls.format_spec()
38
- parsed = parser.parseString(content)
39
- data = {field: parsed[token].strip() for field, token in cls.field_mappings().items()}
40
- return cls(**data)
41
- except ParseException as e:
42
- print(f"Parsing error: {e}")
43
- raise
44
-
45
- def generate(self) -> str:
46
- template, _ = self.format_spec()
47
- for field, token in self.field_mappings().items():
48
- value = getattr(self, field)
49
- template = template.replace(f"{{{token}}}", str(value))
50
- return f"{self.start_token()}\n{template}\n{self.end_token()}"
51
-
52
- class MyFormattedModel(FormattingModel):
53
- name: str
54
- age: int
55
- city: str
56
-
57
- @classmethod
58
- def format_spec(cls) -> tuple[str, ParserElement]:
59
- template = "name: {NAME}\n{AGE} is the age\nlives in {CITY}"
60
- name = Suppress("name:") + Word(alphanums + " ")("NAME") + LineEnd()
61
- age = Word(alphanums)("AGE") + Suppress("is the age") + LineEnd()
62
- city = Suppress("lives in") + SkipTo(LineEnd())("CITY")
63
- parser = name + age + city
64
- return template, parser
65
-
66
- @classmethod
67
- def start_token(cls) -> str:
68
- return "<format>"
69
-
70
- @classmethod
71
- def end_token(cls) -> str:
72
- return "</format>"
73
-
74
- @classmethod
75
- def field_mappings(cls) -> Dict[str, str]:
76
- return {
77
- "name": "NAME",
78
- "age": "AGE",
79
- "city": "CITY"
80
- }
81
-
82
- class CodeFileModel(FormattingModel):
83
- language: str
84
- file_path: str
85
- code: str
86
-
87
- @classmethod
88
- def format_spec(cls) -> tuple[str, ParserElement]:
89
- template = "code_file_model\nfile_path: {FILE_PATH}\n```{LANGUAGE}\n{CODE}\n```"
90
- file_path = Suppress("code_file_model") + LineEnd() + Suppress("file_path:") + SkipTo(LineEnd())("FILE_PATH") + LineEnd()
91
- code_block = Suppress("```") + Word(alphanums)("LANGUAGE") + LineEnd() + Regex(r"(?s).*?(?=```)").set_parse_action(lambda s, l, t: t[0].strip())("CODE") + Suppress("```")
92
- parser = file_path + code_block
93
- return template, parser
94
-
95
- @classmethod
96
- def start_token(cls) -> str:
97
- return "<format>"
98
-
99
- @classmethod
100
- def end_token(cls) -> str:
101
- return "</format>"
102
-
103
- @classmethod
104
- def field_mappings(cls) -> Dict[str, str]:
105
- return {
106
- "file_path": "FILE_PATH",
107
- "language": "LANGUAGE",
108
- "code": "CODE"
109
- }
110
-
111
- if __name__ == "__main__":
112
- # Test MyFormattedModel
113
- model = MyFormattedModel(name="John", age=30, city="Tokyo")
114
- generated = model.generate()
115
- print("Generated MyFormattedModel string:")
116
- print(generated)
117
- print()
118
-
119
- parsed = MyFormattedModel.parse(generated)
120
- print("Parsed MyFormattedModel object:")
121
- print(parsed)
122
- print()
123
-
124
- print("MyFormattedModel Round-trip test:")
125
- assert model == parsed, "MyFormattedModel: Original != Parsed"
126
- print("Passed!")
127
- print()
128
-
129
- # Test CodeFileModel
130
- code_model = CodeFileModel(
131
- language="python",
132
- file_path="src/main.py",
133
- code="def hello():\n print(\"Hello, World!\")"
134
- )
135
- code_generated = code_model.generate()
136
- print("Generated CodeFileModel string:")
137
- print(code_generated)
138
- print()
139
-
140
- code_parsed = CodeFileModel.parse(code_generated)
141
- print("Parsed CodeFileModel object:")
142
- print(code_parsed)
143
- print()
144
-
145
- print("CodeFileModel Round-trip test:")
146
- assert code_model == code_parsed, "CodeFileModel: Original != Parsed"
147
- print("Passed!")
148
-
149
- print("\nAll tests passed successfully!")
@@ -1,159 +0,0 @@
1
- from abc import ABC, abstractmethod
2
- from typing import Dict, List, Type, TypeVar
3
-
4
- from pyparsing import (
5
- Group,
6
- LineEnd,
7
- OneOrMore,
8
- ParseException,
9
- ParserElement,
10
- SkipTo,
11
- Suppress,
12
- )
13
-
14
- from langroid.pydantic_v1 import BaseModel
15
-
16
- T = TypeVar("T", bound="FormattingModel")
17
-
18
-
19
- class FormatElement:
20
- def __init__(self, key: str, prefix: str = "", suffix: str = ""):
21
- self.key = key
22
- self.prefix = prefix
23
- self.suffix = suffix
24
-
25
- def to_parser(self) -> ParserElement:
26
- return Group(
27
- Suppress(self.prefix)
28
- + SkipTo(Suppress(self.suffix + LineEnd())).set_results_name(self.key)
29
- )
30
-
31
- def to_template(self) -> str:
32
- return f"{self.prefix}{{{self.key}}}{self.suffix}\n"
33
-
34
-
35
- class FormattingModel(BaseModel, ABC):
36
- @classmethod
37
- @abstractmethod
38
- def format_elements(cls) -> List[FormatElement]:
39
- pass
40
-
41
- @classmethod
42
- @abstractmethod
43
- def start_token(cls) -> str:
44
- pass
45
-
46
- @classmethod
47
- @abstractmethod
48
- def end_token(cls) -> str:
49
- pass
50
-
51
- @classmethod
52
- def format_spec(cls) -> ParserElement:
53
- return Dict(OneOrMore([elem.to_parser() for elem in cls.format_elements()]))
54
-
55
- @classmethod
56
- def parse(cls: Type[T], text: str) -> T:
57
- content = text.strip()[len(cls.start_token()) : -len(cls.end_token())].strip()
58
- try:
59
- parsed = cls.format_spec().parseString(content, parseAll=True)
60
- data = {
61
- elem.key.lower(): parsed[elem.key][0] for elem in cls.format_elements()
62
- }
63
- return cls(**data)
64
- except ParseException as e:
65
- print(f"Parsing error: {e}")
66
- raise
67
-
68
- def generate(self) -> str:
69
- template = "".join(elem.to_template() for elem in self.format_elements())
70
- for elem in self.format_elements():
71
- value = getattr(self, elem.key.lower())
72
- template = template.replace(f"{{{elem.key}}}", str(value))
73
- return f"{self.start_token()}\n{template.strip()}\n{self.end_token()}"
74
-
75
-
76
- class MyFormattedModel(FormattingModel):
77
- name: str
78
- age: int
79
- city: str
80
-
81
- @classmethod
82
- def format_elements(cls) -> List[FormatElement]:
83
- return [
84
- FormatElement("NAME", prefix="name: ", suffix=""),
85
- FormatElement("AGE", prefix="", suffix=" is the age"),
86
- FormatElement("CITY", prefix="lives in ", suffix=""),
87
- ]
88
-
89
- @classmethod
90
- def start_token(cls) -> str:
91
- return "<format>"
92
-
93
- @classmethod
94
- def end_token(cls) -> str:
95
- return "</format>"
96
-
97
-
98
- class CodeFileModel(FormattingModel):
99
- language: str
100
- file_path: str
101
- code: str
102
-
103
- @classmethod
104
- def format_elements(cls) -> List[FormatElement]:
105
- return [
106
- FormatElement("MODEL", prefix="code_file_model", suffix=""),
107
- FormatElement("FILE_PATH", prefix="file_path: ", suffix=""),
108
- FormatElement("LANGUAGE", prefix="```", suffix=""),
109
- FormatElement("CODE", prefix="", suffix="```"),
110
- ]
111
-
112
- @classmethod
113
- def start_token(cls) -> str:
114
- return "<format>"
115
-
116
- @classmethod
117
- def end_token(cls) -> str:
118
- return "</format>"
119
-
120
-
121
- if __name__ == "__main__":
122
- # Test MyFormattedModel
123
- model = MyFormattedModel(name="John", age=30, city="Tokyo")
124
- generated = model.generate()
125
- print("Generated MyFormattedModel string:")
126
- print(generated)
127
- print()
128
-
129
- parsed = MyFormattedModel.parse(generated)
130
- print("Parsed MyFormattedModel object:")
131
- print(parsed)
132
- print()
133
-
134
- print("MyFormattedModel Round-trip test:")
135
- assert model == parsed, "MyFormattedModel: Original != Parsed"
136
- print("Passed!")
137
- print()
138
-
139
- # Test CodeFileModel
140
- code_model = CodeFileModel(
141
- language="python",
142
- file_path="src/main.py",
143
- code='def hello():\n print("Hello, World!")',
144
- )
145
- code_generated = code_model.generate()
146
- print("Generated CodeFileModel string:")
147
- print(code_generated)
148
- print()
149
-
150
- code_parsed = CodeFileModel.parse(code_generated)
151
- print("Parsed CodeFileModel object:")
152
- print(code_parsed)
153
- print()
154
-
155
- print("CodeFileModel Round-trip test:")
156
- assert code_model == code_parsed, "CodeFileModel: Original != Parsed"
157
- print("Passed!")
158
-
159
- print("\nAll tests passed successfully!")