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,160 @@
1
+ from abc import ABC, abstractmethod
2
+ from pydantic import BaseModel
3
+ import tatsu
4
+ from tatsu.model import ModelBuilderSemantics
5
+
6
+ class FormattingModel(BaseModel, ABC):
7
+ @classmethod
8
+ @abstractmethod
9
+ def format_spec(cls):
10
+ pass
11
+
12
+ @classmethod
13
+ @abstractmethod
14
+ def parse_spec(cls):
15
+ pass
16
+
17
+ @classmethod
18
+ @abstractmethod
19
+ def start_token(cls) -> str:
20
+ pass
21
+
22
+ @classmethod
23
+ @abstractmethod
24
+ def end_token(cls) -> str:
25
+ pass
26
+
27
+ @classmethod
28
+ def format(cls, instance: 'FormattingModel') -> str:
29
+ spec = cls.format_spec()
30
+ formatted = spec.format(**instance.dict())
31
+ return f"{cls.start_token()}\n{formatted}\n{cls.end_token()}"
32
+
33
+ @classmethod
34
+ def parse(cls, formatted_string: str) -> 'FormattingModel':
35
+ lines = formatted_string.strip().split('\n')
36
+ if lines[0] != cls.start_token() or lines[-1] != cls.end_token():
37
+ raise ValueError("Invalid start or end token")
38
+ content = '\n'.join(lines[1:-1])
39
+
40
+ parser = tatsu.compile(cls.parse_spec())
41
+ ast = parser.parse(content)
42
+ return cls(**ast)
43
+
44
+ class CodeFileModel(FormattingModel):
45
+ language: str
46
+ file_path: str
47
+ code: str
48
+
49
+ @classmethod
50
+ def format_spec(cls):
51
+ return "code_file_model\n{file_path}\n```{language}\n{code}\n```"
52
+
53
+ @classmethod
54
+ def parse_spec(cls):
55
+ return '''
56
+ @@grammar::CodeFileModel
57
+
58
+ start = header file_path language code $ ;
59
+
60
+ header = "code_file_model" ~;
61
+ file_path = /[^\n]+/ ~;
62
+ language = "```" /[a-zA-Z]+/ ~;
63
+ code = /(?s).*?(?=```)/ "```" ~;
64
+
65
+ @@whitespace :: /\s*/
66
+ '''
67
+
68
+ @classmethod
69
+ def start_token(cls):
70
+ return '<format>'
71
+
72
+ @classmethod
73
+ def end_token(cls):
74
+ return '</format>'
75
+
76
+ @classmethod
77
+ def parse(cls, formatted_string: str) -> 'CodeFileModel':
78
+ lines = formatted_string.strip().split('\n')
79
+ if lines[0] != cls.start_token() or lines[-1] != cls.end_token():
80
+ raise ValueError("Invalid start or end token")
81
+ content = '\n'.join(lines[1:-1])
82
+
83
+ class CodeFileModelSemantics(ModelBuilderSemantics):
84
+ def file_path(self, ast):
85
+ return ast.strip()
86
+
87
+ def language(self, ast):
88
+ return ast[1].strip()
89
+
90
+ def code(self, ast):
91
+ return ast[0].strip()
92
+
93
+ parser = tatsu.compile(cls.parse_spec(), semantics=CodeFileModelSemantics())
94
+ ast = parser.parse(content)
95
+ return cls(**ast)
96
+
97
+
98
+
99
+ # Test cases
100
+ if __name__ == "__main__":
101
+ # Test formatting
102
+ code_file = CodeFileModel(
103
+ language="Python",
104
+ file_path="src/main.py",
105
+ code="def hello():\n print('Hello, World!')"
106
+ )
107
+ formatted = CodeFileModel.format(code_file)
108
+ expected_format = """<format>
109
+ code_file_model
110
+ src/main.py
111
+ ```Python
112
+ def hello():
113
+ print('Hello, World!')
114
+ ```
115
+ </format>"""
116
+ assert formatted == expected_format, f"Formatting failed. Expected:\n{expected_format}\nGot:\n{formatted}"
117
+ print("Formatting test passed.")
118
+
119
+ # Test parsing
120
+ parsed = CodeFileModel.parse(formatted)
121
+ assert parsed == code_file, f"Parsing failed. Expected:\n{code_file}\nGot:\n{parsed}"
122
+ print("Parsing test passed.")
123
+
124
+ # Test round-trip
125
+ round_trip = CodeFileModel.parse(CodeFileModel.format(code_file))
126
+ assert round_trip == code_file, f"Round-trip failed. Expected:\n{code_file}\nGot:\n{round_trip}"
127
+ print("Round-trip test passed.")
128
+
129
+ # Test with different values
130
+ code_file2 = CodeFileModel(
131
+ language="JavaScript",
132
+ file_path="src/app.js",
133
+ code="function greet() {\n console.log('Hello, World!');\n}"
134
+ )
135
+ formatted2 = CodeFileModel.format(code_file2)
136
+ parsed2 = CodeFileModel.parse(formatted2)
137
+ assert parsed2 == code_file2, f"Parsing failed for different values. Expected:\n{code_file2}\nGot:\n{parsed2}"
138
+ print("Different values test passed.")
139
+
140
+ # Test tolerant parsing
141
+ tolerant_input = """<format>
142
+ code_file_model
143
+ src/main.py
144
+
145
+ ``` Python
146
+ def hello():
147
+ print('Hello, World!')
148
+ ```
149
+ </format>"""
150
+ parsed_tolerant = CodeFileModel.parse(tolerant_input)
151
+ expected_tolerant = CodeFileModel(
152
+ language="Python",
153
+ file_path="src/main.py",
154
+ code="def hello():\n print('Hello, World!')"
155
+ )
156
+ assert parsed_tolerant == expected_tolerant, f"Tolerant parsing failed. Expected:\n{expected_tolerant}\nGot:\n{parsed_tolerant}"
157
+ print("Tolerant parsing test passed.")
158
+
159
+ print("All tests passed successfully!")
160
+
@@ -0,0 +1,217 @@
1
+ from abc import ABC, abstractmethod
2
+ from string import Template
3
+ from typing import Any, 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) -> Dict[str, Any]:
12
+ pass
13
+
14
+ @classmethod
15
+ @abstractmethod
16
+ def parse_spec(cls) -> Dict[str, Any]:
17
+ pass
18
+
19
+ @classmethod
20
+ @abstractmethod
21
+ def start_token(cls) -> str:
22
+ pass
23
+
24
+ @classmethod
25
+ @abstractmethod
26
+ def end_token(cls) -> str:
27
+ pass
28
+
29
+ @classmethod
30
+ def format(cls, instance: "FormattingModel") -> str:
31
+ spec = cls.format_spec()
32
+ template = Template(spec["template"])
33
+ formatted = template.substitute(instance.dict())
34
+ return f"{cls.start_token()}\n{formatted}\n{cls.end_token()}"
35
+
36
+ @classmethod
37
+ def parse(cls, formatted_string: str) -> "FormattingModel":
38
+ spec = cls.parse_spec()
39
+ lines = formatted_string.strip().split("\n")
40
+
41
+ if lines[0] != cls.start_token() or lines[-1] != cls.end_token():
42
+ raise ValueError("Invalid start or end token")
43
+
44
+ content = "\n".join(lines[1:-1])
45
+
46
+ parsed_data = {}
47
+ for field, token in spec["tokens"].items():
48
+ if token:
49
+ parts = content.split(token, 1)
50
+ if len(parts) > 1:
51
+ value = parts[1].split("\n")[0].strip()
52
+ else:
53
+ raise ValueError(
54
+ f"Could not find token '{token}' for field '{field}'"
55
+ )
56
+ else:
57
+ # For fields without a specific token (like age in our example)
58
+ value = (
59
+ content.split("\n")[spec["field_order"].index(field)]
60
+ .split(" ")[0]
61
+ .strip()
62
+ )
63
+
64
+ # Convert to appropriate type based on the model's field types
65
+ field_type = cls.__annotations__[field]
66
+ if field_type == int:
67
+ parsed_data[field] = int(value)
68
+ elif field_type == float:
69
+ parsed_data[field] = float(value)
70
+ else:
71
+ parsed_data[field] = value
72
+
73
+ return cls(**parsed_data)
74
+
75
+
76
+ class PersonModel(FormattingModel):
77
+ name: str
78
+ age: int
79
+ city: str
80
+
81
+ @classmethod
82
+ def format_spec(cls):
83
+ return {"template": "name: $name\n$age is the age\nlives in $city"}
84
+
85
+ @classmethod
86
+ def parse_spec(cls):
87
+ return {
88
+ "template": "name: $name\n$age is the age\nlives in $city",
89
+ "tokens": {"name": "name: ", "age": "", "city": "lives in "},
90
+ "field_order": ["name", "age", "city"],
91
+ }
92
+
93
+ @classmethod
94
+ def start_token(cls):
95
+ return "<format>"
96
+
97
+ @classmethod
98
+ def end_token(cls):
99
+ return "</format>"
100
+
101
+
102
+ # Test cases
103
+ if __name__ == "__main__":
104
+ # Test formatting
105
+ person = PersonModel(name="John", age=30, city="Tokyo")
106
+ formatted = PersonModel.format(person)
107
+ expected_format = """<format>
108
+ name: John
109
+ 30 is the age
110
+ lives in Tokyo
111
+ </format>"""
112
+ assert (
113
+ formatted == expected_format
114
+ ), f"Formatting failed. Expected:\n{expected_format}\nGot:\n{formatted}"
115
+ print("Formatting test passed.")
116
+
117
+ # Test parsing
118
+ parsed = PersonModel.parse(formatted)
119
+ assert parsed == person, f"Parsing failed. Expected:\n{person}\nGot:\n{parsed}"
120
+ print("Parsing test passed.")
121
+
122
+ # Test round-trip
123
+ round_trip = PersonModel.parse(PersonModel.format(person))
124
+ assert (
125
+ round_trip == person
126
+ ), f"Round-trip failed. Expected:\n{person}\nGot:\n{round_trip}"
127
+ print("Round-trip test passed.")
128
+
129
+ # Test with different values
130
+ person2 = PersonModel(name="Alice", age=25, city="New York")
131
+ formatted2 = PersonModel.format(person2)
132
+ parsed2 = PersonModel.parse(formatted2)
133
+ assert (
134
+ parsed2 == person2
135
+ ), f"Parsing failed for different values. Expected:\n{person2}\nGot:\n{parsed2}"
136
+ print("Different values test passed.")
137
+
138
+ print("All tests passed successfully!")
139
+
140
+ class CodeFileModel(FormattingModel):
141
+ language: str
142
+ file_path: str
143
+ code: str
144
+
145
+ @classmethod
146
+ def format_spec(cls):
147
+ return {"template": "code_file_model\n$file_path\n```$language\n$code\n```"}
148
+
149
+ @classmethod
150
+ def parse_spec(cls):
151
+ return {
152
+ "template": "code_file_model\n$file_path\n```$language\n$code\n```",
153
+ "tokens": {
154
+ "language": "```",
155
+ "file_path": "code_file_model\n",
156
+ "code": "\n",
157
+ },
158
+ "field_order": ["file_path", "language", "code"],
159
+ }
160
+
161
+ @classmethod
162
+ def start_token(cls):
163
+ return "<format>"
164
+
165
+ @classmethod
166
+ def end_token(cls):
167
+ return "</format>"
168
+
169
+ # Test formatting
170
+
171
+ code_file = CodeFileModel(
172
+ language="Python",
173
+ file_path="src/main.py",
174
+ code="def hello():\n print('Hello, World!')",
175
+ )
176
+ formatted = CodeFileModel.format(code_file)
177
+ expected_format = """<format>
178
+ code_file_model
179
+ src/main.py
180
+ ```Python
181
+ def hello():
182
+ print('Hello, World!')
183
+ ```
184
+ </format>"""
185
+ assert (
186
+ formatted == expected_format
187
+ ), f"Formatting failed. Expected:\n{expected_format}\nGot:\n{formatted}"
188
+ print("Formatting test passed.")
189
+
190
+ # Test parsing
191
+ parsed = CodeFileModel.parse(formatted)
192
+ assert (
193
+ parsed == code_file
194
+ ), f"Parsing failed. Expected:\n{code_file}\nGot:\n{parsed}"
195
+ print("Parsing test passed.")
196
+
197
+ # Test round-trip
198
+ round_trip = CodeFileModel.parse(CodeFileModel.format(code_file))
199
+ assert (
200
+ round_trip == code_file
201
+ ), f"Round-trip failed. Expected:\n{code_file}\nGot:\n{round_trip}"
202
+ print("Round-trip test passed.")
203
+
204
+ # Test with different values
205
+ code_file2 = CodeFileModel(
206
+ language="JavaScript",
207
+ file_path="src/app.js",
208
+ code="function greet() {\n console.log('Hello, World!');\n}",
209
+ )
210
+ formatted2 = CodeFileModel.format(code_file2)
211
+ parsed2 = CodeFileModel.parse(formatted2)
212
+ assert (
213
+ parsed2 == code_file2
214
+ ), f"Parsing failed for different values. Expected:\n{code_file2}\nGot:\n{parsed2}"
215
+ print("Different values test passed.")
216
+
217
+ print("All tests passed successfully!")
@@ -0,0 +1,200 @@
1
+ from abc import ABC, abstractmethod
2
+ from pydantic import BaseModel
3
+ from string import Template
4
+ from typing import Dict, Any
5
+
6
+ class FormattingModel(BaseModel, ABC):
7
+ @classmethod
8
+ @abstractmethod
9
+ def format_spec(cls) -> Dict[str, Any]:
10
+ pass
11
+
12
+ @classmethod
13
+ @abstractmethod
14
+ def parse_spec(cls) -> Dict[str, Any]:
15
+ pass
16
+
17
+ @classmethod
18
+ @abstractmethod
19
+ def start_token(cls) -> str:
20
+ pass
21
+
22
+ @classmethod
23
+ @abstractmethod
24
+ def end_token(cls) -> str:
25
+ pass
26
+
27
+ @classmethod
28
+ def format(cls, instance: 'FormattingModel') -> str:
29
+ spec = cls.format_spec()
30
+ template = Template(spec['template'])
31
+ formatted = template.substitute(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
+ spec = cls.parse_spec()
37
+ lines = formatted_string.strip().split('\n')
38
+
39
+ if lines[0] != cls.start_token() or lines[-1] != cls.end_token():
40
+ raise ValueError("Invalid start or end token")
41
+
42
+ content = '\n'.join(lines[1:-1])
43
+
44
+ parsed_data = {}
45
+ for field, token in spec['tokens'].items():
46
+ if token:
47
+ parts = content.split(token, 1)
48
+ if len(parts) > 1:
49
+ value = parts[1].split('\n')[0].strip()
50
+ else:
51
+ raise ValueError(f"Could not find token '{token}' for field '{field}'")
52
+ else:
53
+ # For fields without a specific token (like age in our example)
54
+ value = content.split('\n')[spec['field_order'].index(field)].split(' ')[0].strip()
55
+
56
+ # Convert to appropriate type based on the model's field types
57
+ field_type = cls.__annotations__[field]
58
+ if field_type == int:
59
+ parsed_data[field] = int(value)
60
+ elif field_type == float:
61
+ parsed_data[field] = float(value)
62
+ else:
63
+ parsed_data[field] = value
64
+
65
+ return cls(**parsed_data)
66
+
67
+ class PersonModel(FormattingModel):
68
+ name: str
69
+ age: int
70
+ city: str
71
+
72
+ @classmethod
73
+ def format_spec(cls):
74
+ return {
75
+ 'template': 'name: $name\n$age is the age\nlives in $city'
76
+ }
77
+
78
+ @classmethod
79
+ def parse_spec(cls):
80
+ return {
81
+ 'template': 'name: $name\n$age is the age\nlives in $city',
82
+ 'tokens': {
83
+ 'name': 'name: ',
84
+ 'age': '',
85
+ 'city': 'lives in '
86
+ },
87
+ 'field_order': ['name', 'age', 'city']
88
+ }
89
+
90
+ @classmethod
91
+ def start_token(cls):
92
+ return '<format>'
93
+
94
+ @classmethod
95
+ def end_token(cls):
96
+ return '</format>'
97
+
98
+ # Test cases
99
+ if __name__ == "__main__":
100
+ # Test formatting
101
+ person = PersonModel(name="John", age=30, city="Tokyo")
102
+ formatted = PersonModel.format(person)
103
+ expected_format = """<format>
104
+ name: John
105
+ 30 is the age
106
+ lives in Tokyo
107
+ </format>"""
108
+ assert formatted == expected_format, f"Formatting failed. Expected:\n{expected_format}\nGot:\n{formatted}"
109
+ print("Formatting test passed.")
110
+
111
+ # Test parsing
112
+ parsed = PersonModel.parse(formatted)
113
+ assert parsed == person, f"Parsing failed. Expected:\n{person}\nGot:\n{parsed}"
114
+ print("Parsing test passed.")
115
+
116
+ # Test round-trip
117
+ round_trip = PersonModel.parse(PersonModel.format(person))
118
+ assert round_trip == person, f"Round-trip failed. Expected:\n{person}\nGot:\n{round_trip}"
119
+ print("Round-trip test passed.")
120
+
121
+ # Test with different values
122
+ person2 = PersonModel(name="Alice", age=25, city="New York")
123
+ formatted2 = PersonModel.format(person2)
124
+ parsed2 = PersonModel.parse(formatted2)
125
+ assert parsed2 == person2, f"Parsing failed for different values. Expected:\n{person2}\nGot:\n{parsed2}"
126
+ print("Different values test passed.")
127
+
128
+ print("All tests passed successfully!")
129
+
130
+ class CodeFileModel(FormattingModel):
131
+ language: str
132
+ file_path: str
133
+ code: str
134
+
135
+ @classmethod
136
+ def format_spec(cls):
137
+ return {
138
+ 'template': 'code_file_model\n$file_path\n```$language\n$code\n```'
139
+ }
140
+
141
+ @classmethod
142
+ def parse_spec(cls):
143
+ return {
144
+ 'template': 'code_file_model\n$file_path\n```$language\n$code\n```',
145
+ 'tokens': {
146
+ 'language': '```',
147
+ 'file_path': 'code_file_model\n',
148
+ 'code': '\n'
149
+ },
150
+ 'field_order': ['file_path', 'language', 'code']
151
+ }
152
+
153
+ @classmethod
154
+ def start_token(cls):
155
+ return '<format>'
156
+
157
+ @classmethod
158
+ def end_token(cls):
159
+ return '</format>'
160
+
161
+ # Test formatting
162
+ code_file = CodeFileModel(
163
+ language="Python",
164
+ file_path="src/main.py",
165
+ code="def hello():\n print('Hello, World!')"
166
+ )
167
+ formatted = CodeFileModel.format(code_file)
168
+ expected_format = """<format>
169
+ code_file_model
170
+ src/main.py
171
+ ```Python
172
+ def hello():
173
+ print('Hello, World!')
174
+ ```
175
+ </format>"""
176
+ assert formatted == expected_format, f"Formatting failed. Expected:\n{expected_format}\nGot:\n{formatted}"
177
+ print("Formatting test passed.")
178
+
179
+ # Test parsing
180
+ parsed = CodeFileModel.parse(formatted)
181
+ assert parsed == code_file, f"Parsing failed. Expected:\n{code_file}\nGot:\n{parsed}"
182
+ print("Parsing test passed.")
183
+
184
+ # Test round-trip
185
+ round_trip = CodeFileModel.parse(CodeFileModel.format(code_file))
186
+ assert round_trip == code_file, f"Round-trip failed. Expected:\n{code_file}\nGot:\n{round_trip}"
187
+ print("Round-trip test passed.")
188
+
189
+ # Test with different values
190
+ code_file2 = CodeFileModel(
191
+ language="JavaScript",
192
+ file_path="src/app.js",
193
+ code="function greet() {\n console.log('Hello, World!');\n}"
194
+ )
195
+ formatted2 = CodeFileModel.format(code_file2)
196
+ parsed2 = CodeFileModel.parse(formatted2)
197
+ assert parsed2 == code_file2, f"Parsing failed for different values. Expected:\n{code_file2}\nGot:\n{parsed2}"
198
+ print("Different values test passed.")
199
+
200
+ print("All tests passed successfully!")