langroid 0.16.5__py3-none-any.whl → 0.16.7__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 (80) hide show
  1. langroid/agent/md_tool_message_grammar.py +455 -0
  2. langroid/agent/tools/code_file_tool_parse.py +150 -0
  3. langroid/agent/tools/code_file_tool_pyparsing.py +194 -0
  4. langroid/agent/tools/code_file_tool_pyparsing2.py +199 -0
  5. langroid/agent/tools/formatted_model_custom.py +150 -0
  6. langroid/agent/tools/formatted_model_custom2.py +168 -0
  7. langroid/agent/tools/formatted_model_custom3.py +279 -0
  8. langroid/agent/tools/formatted_model_custom4.py +395 -0
  9. langroid/agent/tools/formatted_model_jinja.py +133 -0
  10. langroid/agent/tools/formatted_model_jinja.py-e +122 -0
  11. langroid/agent/tools/formatted_model_jinja2.py +145 -0
  12. langroid/agent/tools/formatted_model_jinja2.py-e +135 -0
  13. langroid/agent/tools/formatted_model_lark.py +0 -0
  14. langroid/agent/tools/formatted_model_lark2.py +168 -0
  15. langroid/agent/tools/formatted_model_parse.py +105 -0
  16. langroid/agent/tools/formatted_model_parse.py-e +98 -0
  17. langroid/agent/tools/formatted_model_parse2.py +113 -0
  18. langroid/agent/tools/formatted_model_parse2.py-e +109 -0
  19. langroid/agent/tools/formatted_model_parse3.py +114 -0
  20. langroid/agent/tools/formatted_model_parse3.py-e +110 -0
  21. langroid/agent/tools/formatted_model_parsimon.py +194 -0
  22. langroid/agent/tools/formatted_model_parsimon.py-e +186 -0
  23. langroid/agent/tools/formatted_model_pyparsing.py +169 -0
  24. langroid/agent/tools/formatted_model_pyparsing.py-e +149 -0
  25. langroid/agent/tools/formatted_model_pyparsing2.py +159 -0
  26. langroid/agent/tools/formatted_model_pyparsing2.py-e +143 -0
  27. langroid/agent/tools/formatted_model_pyparsing3.py +133 -0
  28. langroid/agent/tools/formatted_model_pyparsing3.py-e +121 -0
  29. langroid/agent/tools/formatted_model_pyparsing4.py +213 -0
  30. langroid/agent/tools/formatted_model_pyparsing4.py-e +176 -0
  31. langroid/agent/tools/formatted_model_pyparsing5.py +173 -0
  32. langroid/agent/tools/formatted_model_pyparsing5.py-e +142 -0
  33. langroid/agent/tools/formatted_model_regex.py +246 -0
  34. langroid/agent/tools/formatted_model_regex.py-e +248 -0
  35. langroid/agent/tools/formatted_model_regex2.py +250 -0
  36. langroid/agent/tools/formatted_model_regex2.py-e +253 -0
  37. langroid/agent/tools/formatted_model_tatsu.py +172 -0
  38. langroid/agent/tools/formatted_model_tatsu.py-e +160 -0
  39. langroid/agent/tools/formatted_model_template.py +217 -0
  40. langroid/agent/tools/formatted_model_template.py-e +200 -0
  41. langroid/agent/tools/formatted_model_xml.py +178 -0
  42. langroid/agent/tools/formatted_model_xml2.py +178 -0
  43. langroid/agent/tools/formatted_model_xml3.py +132 -0
  44. langroid/agent/tools/formatted_model_xml4.py +130 -0
  45. langroid/agent/tools/formatted_model_xml5.py +130 -0
  46. langroid/agent/tools/formatted_model_xml6.py +113 -0
  47. langroid/agent/tools/formatted_model_xml7.py +117 -0
  48. langroid/agent/tools/formatted_model_xml8.py +164 -0
  49. langroid/agent/tools/generic_tool.py +165 -0
  50. langroid/agent/tools/generic_tool_tatsu.py +275 -0
  51. langroid/agent/tools/grammar_based_model.py +132 -0
  52. langroid/agent/tools/grammar_based_model.py-e +128 -0
  53. langroid/agent/tools/grammar_based_model_lark.py +156 -0
  54. langroid/agent/tools/grammar_based_model_lark.py-e +153 -0
  55. langroid/agent/tools/grammar_based_model_parse.py +86 -0
  56. langroid/agent/tools/grammar_based_model_parse.py-e +80 -0
  57. langroid/agent/tools/grammar_based_model_parsimonious.py +129 -0
  58. langroid/agent/tools/grammar_based_model_parsimonious.py-e +120 -0
  59. langroid/agent/tools/grammar_based_model_pyparsing.py +105 -0
  60. langroid/agent/tools/grammar_based_model_pyparsing.py-e +103 -0
  61. langroid/agent/tools/grammar_based_model_regex.py +139 -0
  62. langroid/agent/tools/grammar_based_model_regex.py-e +130 -0
  63. langroid/agent/tools/grammar_based_model_regex2.py +124 -0
  64. langroid/agent/tools/grammar_based_model_regex2.py-e +116 -0
  65. langroid/agent/tools/grammar_based_model_tatsu.py +80 -0
  66. langroid/agent/tools/grammar_based_model_tatsu.py-e +77 -0
  67. langroid/agent/tools/lark_earley_example.py +135 -0
  68. langroid/agent/tools/lark_earley_example.py-e +117 -0
  69. langroid/agent/tools/lark_example.py +72 -0
  70. langroid/agent/tools/parse_example.py +76 -0
  71. langroid/agent/tools/parse_example2.py +87 -0
  72. langroid/agent/tools/parse_example3.py +42 -0
  73. langroid/agent/tools/parse_test.py +791 -0
  74. langroid/agent/xml_tool_message.py +106 -0
  75. langroid/language_models/openai_gpt.py +6 -1
  76. {langroid-0.16.5.dist-info → langroid-0.16.7.dist-info}/METADATA +1 -1
  77. {langroid-0.16.5.dist-info → langroid-0.16.7.dist-info}/RECORD +80 -6
  78. pyproject.toml +1 -1
  79. {langroid-0.16.5.dist-info → langroid-0.16.7.dist-info}/LICENSE +0 -0
  80. {langroid-0.16.5.dist-info → langroid-0.16.7.dist-info}/WHEEL +0 -0
@@ -0,0 +1,213 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import List, Tuple
3
+
4
+ from pyparsing import (
5
+ LineEnd,
6
+ Literal,
7
+ Optional,
8
+ SkipTo,
9
+ Suppress,
10
+ White,
11
+ )
12
+
13
+ from langroid.pydantic_v1 import BaseModel
14
+
15
+
16
+ class FormattingModel(BaseModel, ABC):
17
+ @classmethod
18
+ @abstractmethod
19
+ def spec(cls) -> List[Tuple[str, str, dict]]:
20
+ pass
21
+
22
+ @classmethod
23
+ def format_spec(cls):
24
+ format_str = ""
25
+ for literal, field, options in cls.spec():
26
+ if field:
27
+ if options.get("multiline", False):
28
+ format_str += f"{{{field}}}\n"
29
+ else:
30
+ format_str += f"{{{field}}}"
31
+ else:
32
+ format_str += literal
33
+ return format_str
34
+
35
+ @classmethod
36
+ def parse_spec(cls):
37
+ parser = None
38
+ for literal, field, options in cls.spec():
39
+ if literal:
40
+ element = Suppress(Optional(White())) + Literal(literal)
41
+ else:
42
+ if options.get("multiline", False):
43
+ end_marker = Suppress(Optional(White())) + Literal(
44
+ options.get("end_marker", "```")
45
+ )
46
+ element = SkipTo(end_marker).setParseAction(
47
+ lambda s, l, t: t[0].strip()
48
+ )
49
+ else:
50
+ element = SkipTo(LineEnd()).setParseAction(
51
+ lambda s, l, t: t[0].strip()
52
+ )
53
+ if field:
54
+ element = element.setResultsName(field)
55
+ if "parse_action" in options:
56
+ element = element.setParseAction(options["parse_action"])
57
+ parser = element if parser is None else parser + element
58
+ return parser
59
+
60
+ @classmethod
61
+ def start_token(cls) -> str:
62
+ return "<format>"
63
+
64
+ @classmethod
65
+ def end_token(cls) -> str:
66
+ return "</format>"
67
+
68
+ @classmethod
69
+ def format(cls, instance: "FormattingModel") -> str:
70
+ spec = cls.format_spec()
71
+ formatted = spec.format(**instance.dict())
72
+ return f"{cls.start_token()}\n{formatted.rstrip()}\n{cls.end_token()}"
73
+
74
+ @classmethod
75
+ def parse(cls, formatted_string: str) -> "FormattingModel":
76
+ lines = formatted_string.strip().split("\n")
77
+ if lines[0] != cls.start_token() or lines[-1] != cls.end_token():
78
+ raise ValueError("Invalid start or end token")
79
+ content = "\n".join(lines[1:-1])
80
+
81
+ spec = cls.parse_spec()
82
+ parsed = spec.parseString(content, parseAll=True)
83
+ return cls(**parsed.asDict())
84
+
85
+
86
+ class CodeFileModel(FormattingModel):
87
+ language: str
88
+ file_path: str
89
+ code: str
90
+
91
+ @classmethod
92
+ def spec(cls):
93
+ return [
94
+ ("code_file_model\n", "", {}),
95
+ ("", "file_path", {"single_line": True}),
96
+ ("\n```", "", {}),
97
+ ("", "language", {"single_line": True}),
98
+ ("\n", "", {}),
99
+ (
100
+ "",
101
+ "code",
102
+ {
103
+ "multiline": True,
104
+ "end_marker": "```",
105
+ "parse_action": lambda s, l, t: t[0].rstrip(),
106
+ },
107
+ ),
108
+ ("\n```", "", {}),
109
+ ]
110
+
111
+
112
+ if __name__ == "__main__":
113
+ # Test formatting
114
+ code_file = CodeFileModel(
115
+ language="Python",
116
+ file_path="src/main.py",
117
+ code="def hello():\n print('Hello, World!')",
118
+ )
119
+ formatted = CodeFileModel.format(code_file)
120
+ expected_format = """<format>
121
+ code_file_model
122
+ src/main.py
123
+ ```Python
124
+ def hello():
125
+ print('Hello, World!')
126
+ ```
127
+ </format>"""
128
+
129
+ # Compare ignoring whitespace
130
+ assert "".join(formatted.split()) == "".join(
131
+ expected_format.split()
132
+ ), f"Formatting failed. Expected:\n{expected_format}\nGot:\n{formatted}"
133
+ print("Formatting test passed.")
134
+
135
+ # Test parsing
136
+ parsed = CodeFileModel.parse(formatted)
137
+ assert (
138
+ parsed == code_file
139
+ ), f"Parsing failed. Expected:\n{code_file}\nGot:\n{parsed}"
140
+ print("Parsing test passed.")
141
+
142
+ # Test round-trip
143
+ round_trip = CodeFileModel.parse(CodeFileModel.format(code_file))
144
+ assert (
145
+ round_trip == code_file
146
+ ), f"Round-trip failed. Expected:\n{code_file}\nGot:\n{round_trip}"
147
+ print("Round-trip test passed.")
148
+
149
+ # Test with different values
150
+ code_file2 = CodeFileModel(
151
+ language="JavaScript",
152
+ file_path="src/app.js",
153
+ code="function greet() {\n console.log('Hello, World!');\n}",
154
+ )
155
+ formatted2 = CodeFileModel.format(code_file2)
156
+ parsed2 = CodeFileModel.parse(formatted2)
157
+ assert (
158
+ parsed2 == code_file2
159
+ ), f"Parsing failed for different values. Expected:\n{code_file2}\nGot:\n{parsed2}"
160
+ print("Different values test passed.")
161
+
162
+ print("All tests passed successfully!")
163
+
164
+ # Test cases
165
+ if __name__ == "__main__":
166
+ # Test formatting
167
+ code_file = CodeFileModel(
168
+ language="Python",
169
+ file_path="src/main.py",
170
+ code="def hello():\n print('Hello, World!')",
171
+ )
172
+ formatted = CodeFileModel.format(code_file)
173
+ expected_format = """<format>
174
+ code_file_model
175
+ src/main.py
176
+ ```Python
177
+ def hello():
178
+ print('Hello, World!')
179
+ ```
180
+ </format>"""
181
+ assert (
182
+ formatted == expected_format
183
+ ), f"Formatting failed. Expected:\n{expected_format}\nGot:\n{formatted}"
184
+ print("Formatting test passed.")
185
+
186
+ # Test parsing
187
+ parsed = CodeFileModel.parse(formatted)
188
+ assert (
189
+ parsed == code_file
190
+ ), f"Parsing failed. Expected:\n{code_file}\nGot:\n{parsed}"
191
+ print("Parsing test passed.")
192
+
193
+ # Test round-trip
194
+ round_trip = CodeFileModel.parse(CodeFileModel.format(code_file))
195
+ assert (
196
+ round_trip == code_file
197
+ ), f"Round-trip failed. Expected:\n{code_file}\nGot:\n{round_trip}"
198
+ print("Round-trip test passed.")
199
+
200
+ # Test with different values
201
+ code_file2 = CodeFileModel(
202
+ language="JavaScript",
203
+ file_path="src/app.js",
204
+ code="function greet() {\n console.log('Hello, World!');\n}",
205
+ )
206
+ formatted2 = CodeFileModel.format(code_file2)
207
+ parsed2 = CodeFileModel.parse(formatted2)
208
+ assert (
209
+ parsed2 == code_file2
210
+ ), f"Parsing failed for different values. Expected:\n{code_file2}\nGot:\n{parsed2}"
211
+ print("Different values test passed.")
212
+
213
+ print("All tests passed successfully!")
@@ -0,0 +1,176 @@
1
+ from abc import ABC, abstractmethod
2
+ from pydantic import BaseModel
3
+ from pyparsing import (
4
+ Word, alphas, alphanums, Suppress, OneOrMore,
5
+ LineEnd, SkipTo, ParseResults, Optional, Literal, White
6
+ )
7
+ from typing import List, Tuple
8
+
9
+ class FormattingModel(BaseModel, ABC):
10
+ @classmethod
11
+ @abstractmethod
12
+ def spec(cls) -> List[Tuple[str, str, dict]]:
13
+ pass
14
+
15
+ @classmethod
16
+ def format_spec(cls):
17
+ format_str = ''
18
+ for literal, field, options in cls.spec():
19
+ if field:
20
+ if options.get('multiline', False):
21
+ format_str += f"{{{field}}}\n"
22
+ else:
23
+ format_str += f"{{{field}}}"
24
+ else:
25
+ format_str += literal
26
+ return format_str
27
+
28
+ @classmethod
29
+ def parse_spec(cls):
30
+ parser = None
31
+ for literal, field, options in cls.spec():
32
+ if literal:
33
+ element = Suppress(Optional(White())) + Literal(literal)
34
+ else:
35
+ if options.get('multiline', False):
36
+ end_marker = Suppress(Optional(White())) + Literal(options.get('end_marker', '```'))
37
+ element = SkipTo(end_marker).setParseAction(lambda s, l, t: t[0].strip())
38
+ else:
39
+ element = SkipTo(LineEnd()).setParseAction(lambda s, l, t: t[0].strip())
40
+ if field:
41
+ element = element.setResultsName(field)
42
+ if 'parse_action' in options:
43
+ element = element.setParseAction(options['parse_action'])
44
+ parser = element if parser is None else parser + element
45
+ return parser
46
+
47
+ @classmethod
48
+ def start_token(cls) -> str:
49
+ return '<format>'
50
+
51
+ @classmethod
52
+ def end_token(cls) -> str:
53
+ return '</format>'
54
+
55
+ @classmethod
56
+ def format(cls, instance: 'FormattingModel') -> str:
57
+ spec = cls.format_spec()
58
+ formatted = spec.format(**instance.dict())
59
+ return f"{cls.start_token()}\n{formatted.rstrip()}\n{cls.end_token()}"
60
+
61
+ @classmethod
62
+ def parse(cls, formatted_string: str) -> 'FormattingModel':
63
+ lines = formatted_string.strip().split('\n')
64
+ if lines[0] != cls.start_token() or lines[-1] != cls.end_token():
65
+ raise ValueError("Invalid start or end token")
66
+ content = '\n'.join(lines[1:-1])
67
+
68
+ spec = cls.parse_spec()
69
+ parsed = spec.parseString(content, parseAll=True)
70
+ return cls(**parsed.asDict())
71
+
72
+ class CodeFileModel(FormattingModel):
73
+ language: str
74
+ file_path: str
75
+ code: str
76
+
77
+ @classmethod
78
+ def spec(cls):
79
+ return [
80
+ ("code_file_model\n", '', {}),
81
+ ('', 'file_path', {'single_line': True}),
82
+ ("\n```", '', {}),
83
+ ('', 'language', {'single_line': True}),
84
+ ('\n', '', {}),
85
+ ('', 'code', {'multiline': True, 'end_marker': '```', 'parse_action': lambda s, l, t: t[0].rstrip()}),
86
+ ('\n```', '', {})
87
+ ]
88
+
89
+ if __name__ == "__main__":
90
+ # Test formatting
91
+ code_file = CodeFileModel(
92
+ language="Python",
93
+ file_path="src/main.py",
94
+ code="def hello():\n print('Hello, World!')"
95
+ )
96
+ formatted = CodeFileModel.format(code_file)
97
+ expected_format = """<format>
98
+ code_file_model
99
+ src/main.py
100
+ ```Python
101
+ def hello():
102
+ print('Hello, World!')
103
+ ```
104
+ </format>"""
105
+
106
+ # Compare ignoring whitespace
107
+ assert ''.join(formatted.split()) == ''.join(expected_format.split()), \
108
+ f"Formatting failed. Expected:\n{expected_format}\nGot:\n{formatted}"
109
+ print("Formatting test passed.")
110
+
111
+ # Test parsing
112
+ parsed = CodeFileModel.parse(formatted)
113
+ assert parsed == code_file, f"Parsing failed. Expected:\n{code_file}\nGot:\n{parsed}"
114
+ print("Parsing test passed.")
115
+
116
+ # Test round-trip
117
+ round_trip = CodeFileModel.parse(CodeFileModel.format(code_file))
118
+ assert round_trip == code_file, f"Round-trip failed. Expected:\n{code_file}\nGot:\n{round_trip}"
119
+ print("Round-trip test passed.")
120
+
121
+ # Test with different values
122
+ code_file2 = CodeFileModel(
123
+ language="JavaScript",
124
+ file_path="src/app.js",
125
+ code="function greet() {\n console.log('Hello, World!');\n}"
126
+ )
127
+ formatted2 = CodeFileModel.format(code_file2)
128
+ parsed2 = CodeFileModel.parse(formatted2)
129
+ assert parsed2 == code_file2, f"Parsing failed for different values. Expected:\n{code_file2}\nGot:\n{parsed2}"
130
+ print("Different values test passed.")
131
+
132
+ print("All tests passed successfully!")
133
+
134
+ # Test cases
135
+ if __name__ == "__main__":
136
+ # Test formatting
137
+ code_file = CodeFileModel(
138
+ language="Python",
139
+ file_path="src/main.py",
140
+ code="def hello():\n print('Hello, World!')"
141
+ )
142
+ formatted = CodeFileModel.format(code_file)
143
+ expected_format = """<format>
144
+ code_file_model
145
+ src/main.py
146
+ ```Python
147
+ def hello():
148
+ print('Hello, World!')
149
+ ```
150
+ </format>"""
151
+ assert formatted == expected_format, f"Formatting failed. Expected:\n{expected_format}\nGot:\n{formatted}"
152
+ print("Formatting test passed.")
153
+
154
+ # Test parsing
155
+ parsed = CodeFileModel.parse(formatted)
156
+ assert parsed == code_file, f"Parsing failed. Expected:\n{code_file}\nGot:\n{parsed}"
157
+ print("Parsing test passed.")
158
+
159
+ # Test round-trip
160
+ round_trip = CodeFileModel.parse(CodeFileModel.format(code_file))
161
+ assert round_trip == code_file, f"Round-trip failed. Expected:\n{code_file}\nGot:\n{round_trip}"
162
+ print("Round-trip test passed.")
163
+
164
+ # Test with different values
165
+ code_file2 = CodeFileModel(
166
+ language="JavaScript",
167
+ file_path="src/app.js",
168
+ code="function greet() {\n console.log('Hello, World!');\n}"
169
+ )
170
+ formatted2 = CodeFileModel.format(code_file2)
171
+ parsed2 = CodeFileModel.parse(formatted2)
172
+ assert parsed2 == code_file2, f"Parsing failed for different values. Expected:\n{code_file2}\nGot:\n{parsed2}"
173
+ print("Different values test passed.")
174
+
175
+ print("All tests passed successfully!")
176
+
@@ -0,0 +1,173 @@
1
+ from abc import ABC, abstractmethod
2
+
3
+ from pyparsing import (
4
+ LineEnd,
5
+ Optional,
6
+ SkipTo,
7
+ Suppress,
8
+ White,
9
+ Word,
10
+ ZeroOrMore,
11
+ alphas,
12
+ )
13
+
14
+ from langroid.pydantic_v1 import BaseModel
15
+
16
+
17
+ class FormattingModel(BaseModel, ABC):
18
+ @classmethod
19
+ @abstractmethod
20
+ def format_spec(cls):
21
+ pass
22
+
23
+ @classmethod
24
+ @abstractmethod
25
+ def parse_spec(cls):
26
+ pass
27
+
28
+ @classmethod
29
+ @abstractmethod
30
+ def start_token(cls) -> str:
31
+ pass
32
+
33
+ @classmethod
34
+ @abstractmethod
35
+ def end_token(cls) -> str:
36
+ pass
37
+
38
+ @classmethod
39
+ def format(cls, instance: "FormattingModel") -> str:
40
+ spec = cls.format_spec()
41
+ formatted = spec.format(**instance.dict())
42
+ return f"{cls.start_token()}\n{formatted}\n{cls.end_token()}"
43
+
44
+ @classmethod
45
+ def parse(cls, formatted_string: str) -> "FormattingModel":
46
+ lines = formatted_string.strip().split("\n")
47
+ if lines[0] != cls.start_token() or lines[-1] != cls.end_token():
48
+ raise ValueError("Invalid start or end token")
49
+ content = "\n".join(lines[1:-1])
50
+
51
+ spec = cls.parse_spec()
52
+ parsed = spec.parseString(content, parseAll=True)
53
+ return cls(**parsed.asDict())
54
+
55
+
56
+ class CodeFileModel(FormattingModel):
57
+ language: str
58
+ file_path: str
59
+ code: str
60
+
61
+ @classmethod
62
+ def format_spec(cls):
63
+ return "code_file_model\n{file_path}\n```{language}\n{code}\n```"
64
+
65
+ @classmethod
66
+ def parse_spec(cls):
67
+ header = Suppress("code_file_model") + ZeroOrMore(LineEnd())
68
+ file_path = (
69
+ SkipTo(LineEnd())
70
+ .setResultsName("file_path")
71
+ .setParseAction(lambda s, l, t: t[0].strip())
72
+ )
73
+ language = (
74
+ Suppress("```")
75
+ + Optional(White())
76
+ + Word(alphas).setResultsName("language")
77
+ + LineEnd()
78
+ )
79
+ code = (
80
+ SkipTo("```")
81
+ .setResultsName("code")
82
+ .setParseAction(lambda s, l, t: t[0].strip())
83
+ )
84
+
85
+ return (
86
+ header
87
+ + file_path
88
+ + ZeroOrMore(LineEnd())
89
+ + language
90
+ + code
91
+ + Suppress("```")
92
+ )
93
+
94
+ @classmethod
95
+ def start_token(cls):
96
+ return "<format>"
97
+
98
+ @classmethod
99
+ def end_token(cls):
100
+ return "</format>"
101
+
102
+
103
+ # Test cases
104
+ if __name__ == "__main__":
105
+ # Test formatting
106
+ code_file = CodeFileModel(
107
+ language="Python",
108
+ file_path="src/main.py",
109
+ code="def hello():\n print('Hello, World!')",
110
+ )
111
+ formatted = CodeFileModel.format(code_file)
112
+ expected_format = """<format>
113
+ code_file_model
114
+ src/main.py
115
+ ```Python
116
+ def hello():
117
+ print('Hello, World!')
118
+ ```
119
+ </format>"""
120
+ assert (
121
+ formatted == expected_format
122
+ ), f"Formatting failed. Expected:\n{expected_format}\nGot:\n{formatted}"
123
+ print("Formatting test passed.")
124
+
125
+ # Test parsing
126
+ parsed = CodeFileModel.parse(formatted)
127
+ assert (
128
+ parsed == code_file
129
+ ), f"Parsing failed. Expected:\n{code_file}\nGot:\n{parsed}"
130
+ print("Parsing test passed.")
131
+
132
+ # Test round-trip
133
+ round_trip = CodeFileModel.parse(CodeFileModel.format(code_file))
134
+ assert (
135
+ round_trip == code_file
136
+ ), f"Round-trip failed. Expected:\n{code_file}\nGot:\n{round_trip}"
137
+ print("Round-trip test passed.")
138
+
139
+ # Test with different values
140
+ code_file2 = CodeFileModel(
141
+ language="JavaScript",
142
+ file_path="src/app.js",
143
+ code="function greet() {\n console.log('Hello, World!');\n}",
144
+ )
145
+ formatted2 = CodeFileModel.format(code_file2)
146
+ parsed2 = CodeFileModel.parse(formatted2)
147
+ assert (
148
+ parsed2 == code_file2
149
+ ), f"Parsing failed for different values. Expected:\n{code_file2}\nGot:\n{parsed2}"
150
+ print("Different values test passed.")
151
+
152
+ # Test tolerant parsing
153
+ tolerant_input = """<format>
154
+ code_file_model
155
+ src/main.py
156
+
157
+ ``` Python
158
+ def hello():
159
+ print('Hello, World!')
160
+ ```
161
+ </format>"""
162
+ parsed_tolerant = CodeFileModel.parse(tolerant_input)
163
+ expected_tolerant = CodeFileModel(
164
+ language="Python",
165
+ file_path="src/main.py",
166
+ code="def hello():\n print('Hello, World!')",
167
+ )
168
+ assert (
169
+ parsed_tolerant == expected_tolerant
170
+ ), f"Tolerant parsing failed. Expected:\n{expected_tolerant}\nGot:\n{parsed_tolerant}"
171
+ print("Tolerant parsing test passed.")
172
+
173
+ print("All tests passed successfully!")
@@ -0,0 +1,142 @@
1
+ from abc import ABC, abstractmethod
2
+ from pydantic import BaseModel
3
+ from pyparsing import (
4
+ Word, alphas, alphanums, Suppress, OneOrMore,
5
+ LineEnd, SkipTo, ParseResults, Optional, Literal, White, ZeroOrMore
6
+ )
7
+ class FormattingModel(BaseModel, ABC):
8
+ @classmethod
9
+ @abstractmethod
10
+ def format_spec(cls):
11
+ pass
12
+
13
+ @classmethod
14
+ @abstractmethod
15
+ def parse_spec(cls):
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
+ def format(cls, instance: 'FormattingModel') -> str:
30
+ spec = cls.format_spec()
31
+ formatted = spec.format(**instance.dict())
32
+ return f"{cls.start_token()}\n{formatted}\n{cls.end_token()}"
33
+
34
+ @classmethod
35
+ def parse(cls, formatted_string: str) -> 'FormattingModel':
36
+ lines = formatted_string.strip().split('\n')
37
+ if lines[0] != cls.start_token() or lines[-1] != cls.end_token():
38
+ raise ValueError("Invalid start or end token")
39
+ content = '\n'.join(lines[1:-1])
40
+
41
+ spec = cls.parse_spec()
42
+ parsed = spec.parseString(content, parseAll=True)
43
+ return cls(**parsed.asDict())
44
+
45
+ class CodeFileModel(FormattingModel):
46
+ language: str
47
+ file_path: str
48
+ code: str
49
+
50
+ @classmethod
51
+ def format_spec(cls):
52
+ return "code_file_model\n{file_path}\n```{language}\n{code}\n```"
53
+
54
+ @classmethod
55
+ def parse_spec(cls):
56
+ header = Suppress("code_file_model") + ZeroOrMore(LineEnd())
57
+ file_path = SkipTo(LineEnd()).setResultsName("file_path").setParseAction(lambda s, l, t: t[0].strip())
58
+ language = Suppress("```") + Optional(White()) + Word(alphas).setResultsName("language") + LineEnd()
59
+ code = SkipTo("```").setResultsName("code").setParseAction(lambda s, l, t: t[0].strip())
60
+
61
+ return (
62
+ header +
63
+ file_path +
64
+ ZeroOrMore(LineEnd()) +
65
+ language +
66
+ code +
67
+ Suppress("```")
68
+ )
69
+
70
+
71
+ @classmethod
72
+ def start_token(cls):
73
+ return '<format>'
74
+
75
+ @classmethod
76
+ def end_token(cls):
77
+ return '</format>'
78
+
79
+ # Test cases
80
+ if __name__ == "__main__":
81
+ # Test formatting
82
+ code_file = CodeFileModel(
83
+ language="Python",
84
+ file_path="src/main.py",
85
+ code="def hello():\n print('Hello, World!')"
86
+ )
87
+ formatted = CodeFileModel.format(code_file)
88
+ expected_format = """<format>
89
+ code_file_model
90
+ src/main.py
91
+ ```Python
92
+ def hello():
93
+ print('Hello, World!')
94
+ ```
95
+ </format>"""
96
+ assert formatted == expected_format, f"Formatting failed. Expected:\n{expected_format}\nGot:\n{formatted}"
97
+ print("Formatting test passed.")
98
+
99
+ # Test parsing
100
+ parsed = CodeFileModel.parse(formatted)
101
+ assert parsed == code_file, f"Parsing failed. Expected:\n{code_file}\nGot:\n{parsed}"
102
+ print("Parsing test passed.")
103
+
104
+ # Test round-trip
105
+ round_trip = CodeFileModel.parse(CodeFileModel.format(code_file))
106
+ assert round_trip == code_file, f"Round-trip failed. Expected:\n{code_file}\nGot:\n{round_trip}"
107
+ print("Round-trip test passed.")
108
+
109
+ # Test with different values
110
+ code_file2 = CodeFileModel(
111
+ language="JavaScript",
112
+ file_path="src/app.js",
113
+ code="function greet() {\n console.log('Hello, World!');\n}"
114
+ )
115
+ formatted2 = CodeFileModel.format(code_file2)
116
+ parsed2 = CodeFileModel.parse(formatted2)
117
+ assert parsed2 == code_file2, f"Parsing failed for different values. Expected:\n{code_file2}\nGot:\n{parsed2}"
118
+ print("Different values test passed.")
119
+
120
+
121
+ # Test tolerant parsing
122
+ tolerant_input = """<format>
123
+ code_file_model
124
+ src/main.py
125
+
126
+ ``` Python
127
+ def hello():
128
+ print('Hello, World!')
129
+ ```
130
+ </format>"""
131
+ parsed_tolerant = CodeFileModel.parse(tolerant_input)
132
+ expected_tolerant = CodeFileModel(
133
+ language="Python",
134
+ file_path="src/main.py",
135
+ code="def hello():\n print('Hello, World!')"
136
+ )
137
+ assert parsed_tolerant == expected_tolerant, f"Tolerant parsing failed. Expected:\n{expected_tolerant}\nGot:\n{parsed_tolerant}"
138
+ print("Tolerant parsing test passed.")
139
+
140
+ print("All tests passed successfully!")
141
+
142
+