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.
- langroid/agent/md_tool_message_grammar.py +455 -0
- langroid/agent/tools/code_file_tool_parse.py +150 -0
- langroid/agent/tools/code_file_tool_pyparsing.py +194 -0
- langroid/agent/tools/code_file_tool_pyparsing2.py +199 -0
- langroid/agent/tools/formatted_model_custom.py +150 -0
- langroid/agent/tools/formatted_model_custom2.py +168 -0
- langroid/agent/tools/formatted_model_custom3.py +279 -0
- langroid/agent/tools/formatted_model_custom4.py +395 -0
- langroid/agent/tools/formatted_model_jinja.py +133 -0
- langroid/agent/tools/formatted_model_jinja.py-e +122 -0
- langroid/agent/tools/formatted_model_jinja2.py +145 -0
- langroid/agent/tools/formatted_model_jinja2.py-e +135 -0
- langroid/agent/tools/formatted_model_lark.py +0 -0
- langroid/agent/tools/formatted_model_lark2.py +168 -0
- langroid/agent/tools/formatted_model_parse.py +105 -0
- langroid/agent/tools/formatted_model_parse.py-e +98 -0
- langroid/agent/tools/formatted_model_parse2.py +113 -0
- langroid/agent/tools/formatted_model_parse2.py-e +109 -0
- langroid/agent/tools/formatted_model_parse3.py +114 -0
- langroid/agent/tools/formatted_model_parse3.py-e +110 -0
- langroid/agent/tools/formatted_model_parsimon.py +194 -0
- langroid/agent/tools/formatted_model_parsimon.py-e +186 -0
- langroid/agent/tools/formatted_model_pyparsing.py +169 -0
- langroid/agent/tools/formatted_model_pyparsing.py-e +149 -0
- langroid/agent/tools/formatted_model_pyparsing2.py +159 -0
- langroid/agent/tools/formatted_model_pyparsing2.py-e +143 -0
- langroid/agent/tools/formatted_model_pyparsing3.py +133 -0
- langroid/agent/tools/formatted_model_pyparsing3.py-e +121 -0
- langroid/agent/tools/formatted_model_pyparsing4.py +213 -0
- langroid/agent/tools/formatted_model_pyparsing4.py-e +176 -0
- langroid/agent/tools/formatted_model_pyparsing5.py +173 -0
- langroid/agent/tools/formatted_model_pyparsing5.py-e +142 -0
- langroid/agent/tools/formatted_model_regex.py +246 -0
- langroid/agent/tools/formatted_model_regex.py-e +248 -0
- langroid/agent/tools/formatted_model_regex2.py +250 -0
- langroid/agent/tools/formatted_model_regex2.py-e +253 -0
- langroid/agent/tools/formatted_model_tatsu.py +172 -0
- langroid/agent/tools/formatted_model_tatsu.py-e +160 -0
- langroid/agent/tools/formatted_model_template.py +217 -0
- langroid/agent/tools/formatted_model_template.py-e +200 -0
- langroid/agent/tools/formatted_model_xml.py +178 -0
- langroid/agent/tools/formatted_model_xml2.py +178 -0
- langroid/agent/tools/formatted_model_xml3.py +132 -0
- langroid/agent/tools/formatted_model_xml4.py +130 -0
- langroid/agent/tools/formatted_model_xml5.py +130 -0
- langroid/agent/tools/formatted_model_xml6.py +113 -0
- langroid/agent/tools/formatted_model_xml7.py +117 -0
- langroid/agent/tools/formatted_model_xml8.py +164 -0
- langroid/agent/tools/generic_tool.py +165 -0
- langroid/agent/tools/generic_tool_tatsu.py +275 -0
- langroid/agent/tools/grammar_based_model.py +132 -0
- langroid/agent/tools/grammar_based_model.py-e +128 -0
- langroid/agent/tools/grammar_based_model_lark.py +156 -0
- langroid/agent/tools/grammar_based_model_lark.py-e +153 -0
- langroid/agent/tools/grammar_based_model_parse.py +86 -0
- langroid/agent/tools/grammar_based_model_parse.py-e +80 -0
- langroid/agent/tools/grammar_based_model_parsimonious.py +129 -0
- langroid/agent/tools/grammar_based_model_parsimonious.py-e +120 -0
- langroid/agent/tools/grammar_based_model_pyparsing.py +105 -0
- langroid/agent/tools/grammar_based_model_pyparsing.py-e +103 -0
- langroid/agent/tools/grammar_based_model_regex.py +139 -0
- langroid/agent/tools/grammar_based_model_regex.py-e +130 -0
- langroid/agent/tools/grammar_based_model_regex2.py +124 -0
- langroid/agent/tools/grammar_based_model_regex2.py-e +116 -0
- langroid/agent/tools/grammar_based_model_tatsu.py +80 -0
- langroid/agent/tools/grammar_based_model_tatsu.py-e +77 -0
- langroid/agent/tools/lark_earley_example.py +135 -0
- langroid/agent/tools/lark_earley_example.py-e +117 -0
- langroid/agent/tools/lark_example.py +72 -0
- langroid/agent/tools/parse_example.py +76 -0
- langroid/agent/tools/parse_example2.py +87 -0
- langroid/agent/tools/parse_example3.py +42 -0
- langroid/agent/tools/parse_test.py +791 -0
- langroid/agent/xml_tool_message.py +106 -0
- langroid/language_models/openai_gpt.py +6 -1
- {langroid-0.16.5.dist-info → langroid-0.16.7.dist-info}/METADATA +1 -1
- {langroid-0.16.5.dist-info → langroid-0.16.7.dist-info}/RECORD +80 -6
- pyproject.toml +1 -1
- {langroid-0.16.5.dist-info → langroid-0.16.7.dist-info}/LICENSE +0 -0
- {langroid-0.16.5.dist-info → langroid-0.16.7.dist-info}/WHEEL +0 -0
@@ -0,0 +1,159 @@
|
|
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!")
|
@@ -0,0 +1,143 @@
|
|
1
|
+
from pydantic import BaseModel
|
2
|
+
from abc import ABC, abstractmethod
|
3
|
+
from typing import Dict, Type, TypeVar, List
|
4
|
+
from pyparsing import (
|
5
|
+
Word, alphanums, Suppress, SkipTo, LineEnd,
|
6
|
+
QuotedString, delimitedList, Group, OneOrMore, ParseException,
|
7
|
+
ParserElement, Regex, Literal, Optional, White, Dict
|
8
|
+
)
|
9
|
+
|
10
|
+
T = TypeVar('T', bound='FormattingModel')
|
11
|
+
|
12
|
+
class FormatElement:
|
13
|
+
def __init__(self, key: str, prefix: str = "", suffix: str = ""):
|
14
|
+
self.key = key
|
15
|
+
self.prefix = prefix
|
16
|
+
self.suffix = suffix
|
17
|
+
|
18
|
+
def to_parser(self) -> ParserElement:
|
19
|
+
return Group(Suppress(self.prefix) + SkipTo(Suppress(self.suffix + LineEnd())).set_results_name(self.key))
|
20
|
+
|
21
|
+
def to_template(self) -> str:
|
22
|
+
return f"{self.prefix}{{{self.key}}}{self.suffix}\n"
|
23
|
+
|
24
|
+
class FormattingModel(BaseModel, ABC):
|
25
|
+
@classmethod
|
26
|
+
@abstractmethod
|
27
|
+
def format_elements(cls) -> List[FormatElement]:
|
28
|
+
pass
|
29
|
+
|
30
|
+
@classmethod
|
31
|
+
@abstractmethod
|
32
|
+
def start_token(cls) -> str:
|
33
|
+
pass
|
34
|
+
|
35
|
+
@classmethod
|
36
|
+
@abstractmethod
|
37
|
+
def end_token(cls) -> str:
|
38
|
+
pass
|
39
|
+
|
40
|
+
@classmethod
|
41
|
+
def format_spec(cls) -> ParserElement:
|
42
|
+
return Dict(OneOrMore([elem.to_parser() for elem in cls.format_elements()]))
|
43
|
+
|
44
|
+
@classmethod
|
45
|
+
def parse(cls: Type[T], text: str) -> T:
|
46
|
+
content = text.strip()[len(cls.start_token()):-len(cls.end_token())].strip()
|
47
|
+
try:
|
48
|
+
parsed = cls.format_spec().parseString(content, parseAll=True)
|
49
|
+
data = {elem.key.lower(): parsed[elem.key][0] for elem in cls.format_elements()}
|
50
|
+
return cls(**data)
|
51
|
+
except ParseException as e:
|
52
|
+
print(f"Parsing error: {e}")
|
53
|
+
raise
|
54
|
+
|
55
|
+
def generate(self) -> str:
|
56
|
+
template = "".join(elem.to_template() for elem in self.format_elements())
|
57
|
+
for elem in self.format_elements():
|
58
|
+
value = getattr(self, elem.key.lower())
|
59
|
+
template = template.replace(f"{{{elem.key}}}", str(value))
|
60
|
+
return f"{self.start_token()}\n{template.strip()}\n{self.end_token()}"
|
61
|
+
|
62
|
+
class MyFormattedModel(FormattingModel):
|
63
|
+
name: str
|
64
|
+
age: int
|
65
|
+
city: str
|
66
|
+
|
67
|
+
@classmethod
|
68
|
+
def format_elements(cls) -> List[FormatElement]:
|
69
|
+
return [
|
70
|
+
FormatElement("NAME", prefix="name: ", suffix=""),
|
71
|
+
FormatElement("AGE", prefix="", suffix=" is the age"),
|
72
|
+
FormatElement("CITY", prefix="lives in ", suffix="")
|
73
|
+
]
|
74
|
+
|
75
|
+
@classmethod
|
76
|
+
def start_token(cls) -> str:
|
77
|
+
return "<format>"
|
78
|
+
|
79
|
+
@classmethod
|
80
|
+
def end_token(cls) -> str:
|
81
|
+
return "</format>"
|
82
|
+
|
83
|
+
class CodeFileModel(FormattingModel):
|
84
|
+
language: str
|
85
|
+
file_path: str
|
86
|
+
code: str
|
87
|
+
|
88
|
+
@classmethod
|
89
|
+
def format_elements(cls) -> List[FormatElement]:
|
90
|
+
return [
|
91
|
+
FormatElement("MODEL", prefix="code_file_model", suffix=""),
|
92
|
+
FormatElement("FILE_PATH", prefix="file_path: ", suffix=""),
|
93
|
+
FormatElement("LANGUAGE", prefix="```", suffix=""),
|
94
|
+
FormatElement("CODE", prefix="", suffix="```")
|
95
|
+
]
|
96
|
+
|
97
|
+
@classmethod
|
98
|
+
def start_token(cls) -> str:
|
99
|
+
return "<format>"
|
100
|
+
|
101
|
+
@classmethod
|
102
|
+
def end_token(cls) -> str:
|
103
|
+
return "</format>"
|
104
|
+
|
105
|
+
if __name__ == "__main__":
|
106
|
+
# Test MyFormattedModel
|
107
|
+
model = MyFormattedModel(name="John", age=30, city="Tokyo")
|
108
|
+
generated = model.generate()
|
109
|
+
print("Generated MyFormattedModel string:")
|
110
|
+
print(generated)
|
111
|
+
print()
|
112
|
+
|
113
|
+
parsed = MyFormattedModel.parse(generated)
|
114
|
+
print("Parsed MyFormattedModel object:")
|
115
|
+
print(parsed)
|
116
|
+
print()
|
117
|
+
|
118
|
+
print("MyFormattedModel Round-trip test:")
|
119
|
+
assert model == parsed, "MyFormattedModel: Original != Parsed"
|
120
|
+
print("Passed!")
|
121
|
+
print()
|
122
|
+
|
123
|
+
# Test CodeFileModel
|
124
|
+
code_model = CodeFileModel(
|
125
|
+
language="python",
|
126
|
+
file_path="src/main.py",
|
127
|
+
code="def hello():\n print(\"Hello, World!\")"
|
128
|
+
)
|
129
|
+
code_generated = code_model.generate()
|
130
|
+
print("Generated CodeFileModel string:")
|
131
|
+
print(code_generated)
|
132
|
+
print()
|
133
|
+
|
134
|
+
code_parsed = CodeFileModel.parse(code_generated)
|
135
|
+
print("Parsed CodeFileModel object:")
|
136
|
+
print(code_parsed)
|
137
|
+
print()
|
138
|
+
|
139
|
+
print("CodeFileModel Round-trip test:")
|
140
|
+
assert code_model == code_parsed, "CodeFileModel: Original != Parsed"
|
141
|
+
print("Passed!")
|
142
|
+
|
143
|
+
print("\nAll tests passed successfully!")
|
@@ -0,0 +1,133 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
|
3
|
+
from pyparsing import (
|
4
|
+
LineEnd,
|
5
|
+
SkipTo,
|
6
|
+
Suppress,
|
7
|
+
Word,
|
8
|
+
alphas,
|
9
|
+
)
|
10
|
+
|
11
|
+
from langroid.pydantic_v1 import BaseModel
|
12
|
+
|
13
|
+
|
14
|
+
class FormattingModel(BaseModel, ABC):
|
15
|
+
@classmethod
|
16
|
+
@abstractmethod
|
17
|
+
def format_spec(cls):
|
18
|
+
pass
|
19
|
+
|
20
|
+
@classmethod
|
21
|
+
@abstractmethod
|
22
|
+
def parse_spec(cls):
|
23
|
+
pass
|
24
|
+
|
25
|
+
@classmethod
|
26
|
+
@abstractmethod
|
27
|
+
def start_token(cls) -> str:
|
28
|
+
pass
|
29
|
+
|
30
|
+
@classmethod
|
31
|
+
@abstractmethod
|
32
|
+
def end_token(cls) -> str:
|
33
|
+
pass
|
34
|
+
|
35
|
+
@classmethod
|
36
|
+
def format(cls, instance: "FormattingModel") -> str:
|
37
|
+
spec = cls.format_spec()
|
38
|
+
formatted = spec.format(**instance.dict())
|
39
|
+
return f"{cls.start_token()}\n{formatted}\n{cls.end_token()}"
|
40
|
+
|
41
|
+
@classmethod
|
42
|
+
def parse(cls, formatted_string: str) -> "FormattingModel":
|
43
|
+
lines = formatted_string.strip().split("\n")
|
44
|
+
if lines[0] != cls.start_token() or lines[-1] != cls.end_token():
|
45
|
+
raise ValueError("Invalid start or end token")
|
46
|
+
content = "\n".join(lines[1:-1])
|
47
|
+
|
48
|
+
spec = cls.parse_spec()
|
49
|
+
parsed = spec.parseString(content, parseAll=True)
|
50
|
+
return cls(**parsed.asDict())
|
51
|
+
|
52
|
+
|
53
|
+
class CodeFileModel(FormattingModel):
|
54
|
+
language: str
|
55
|
+
file_path: str
|
56
|
+
code: str
|
57
|
+
|
58
|
+
@classmethod
|
59
|
+
def format_spec(cls):
|
60
|
+
return "code_file_model\n{file_path}\n```{language}\n{code}\n```"
|
61
|
+
|
62
|
+
@classmethod
|
63
|
+
def parse_spec(cls):
|
64
|
+
header = Suppress("code_file_model") + LineEnd()
|
65
|
+
file_path = SkipTo(LineEnd()).setResultsName("file_path")
|
66
|
+
language = Suppress("```") + Word(alphas).setResultsName("language") + LineEnd()
|
67
|
+
code = (
|
68
|
+
SkipTo("```")
|
69
|
+
.setResultsName("code")
|
70
|
+
.setParseAction(lambda s, l, t: t[0].rstrip())
|
71
|
+
)
|
72
|
+
|
73
|
+
return header + file_path + language + code + Suppress("```")
|
74
|
+
|
75
|
+
@classmethod
|
76
|
+
def start_token(cls):
|
77
|
+
return "<format>"
|
78
|
+
|
79
|
+
@classmethod
|
80
|
+
def end_token(cls):
|
81
|
+
return "</format>"
|
82
|
+
|
83
|
+
|
84
|
+
# Test cases
|
85
|
+
if __name__ == "__main__":
|
86
|
+
# Test formatting
|
87
|
+
code_file = CodeFileModel(
|
88
|
+
language="Python",
|
89
|
+
file_path="src/main.py",
|
90
|
+
code="def hello():\n print('Hello, World!')",
|
91
|
+
)
|
92
|
+
formatted = CodeFileModel.format(code_file)
|
93
|
+
expected_format = """<format>
|
94
|
+
code_file_model
|
95
|
+
src/main.py
|
96
|
+
```Python
|
97
|
+
def hello():
|
98
|
+
print('Hello, World!')
|
99
|
+
```
|
100
|
+
</format>"""
|
101
|
+
assert (
|
102
|
+
formatted == expected_format
|
103
|
+
), f"Formatting failed. Expected:\n{expected_format}\nGot:\n{formatted}"
|
104
|
+
print("Formatting test passed.")
|
105
|
+
|
106
|
+
# Test parsing
|
107
|
+
parsed = CodeFileModel.parse(formatted)
|
108
|
+
assert (
|
109
|
+
parsed == code_file
|
110
|
+
), f"Parsing failed. Expected:\n{code_file}\nGot:\n{parsed}"
|
111
|
+
print("Parsing test passed.")
|
112
|
+
|
113
|
+
# Test round-trip
|
114
|
+
round_trip = CodeFileModel.parse(CodeFileModel.format(code_file))
|
115
|
+
assert (
|
116
|
+
round_trip == code_file
|
117
|
+
), f"Round-trip failed. Expected:\n{code_file}\nGot:\n{round_trip}"
|
118
|
+
print("Round-trip test passed.")
|
119
|
+
|
120
|
+
# Test with different values
|
121
|
+
code_file2 = CodeFileModel(
|
122
|
+
language="JavaScript",
|
123
|
+
file_path="src/app.js",
|
124
|
+
code="function greet() {\n console.log('Hello, World!');\n}",
|
125
|
+
)
|
126
|
+
formatted2 = CodeFileModel.format(code_file2)
|
127
|
+
parsed2 = CodeFileModel.parse(formatted2)
|
128
|
+
assert (
|
129
|
+
parsed2 == code_file2
|
130
|
+
), f"Parsing failed for different values. Expected:\n{code_file2}\nGot:\n{parsed2}"
|
131
|
+
print("Different values test passed.")
|
132
|
+
|
133
|
+
print("All tests passed successfully!")
|
@@ -0,0 +1,121 @@
|
|
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
|
+
|
8
|
+
class FormattingModel(BaseModel, ABC):
|
9
|
+
@classmethod
|
10
|
+
@abstractmethod
|
11
|
+
def format_spec(cls):
|
12
|
+
pass
|
13
|
+
|
14
|
+
@classmethod
|
15
|
+
@abstractmethod
|
16
|
+
def parse_spec(cls):
|
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
|
+
formatted = spec.format(**instance.dict())
|
33
|
+
return f"{cls.start_token()}\n{formatted}\n{cls.end_token()}"
|
34
|
+
|
35
|
+
@classmethod
|
36
|
+
def parse(cls, formatted_string: str) -> 'FormattingModel':
|
37
|
+
lines = formatted_string.strip().split('\n')
|
38
|
+
if lines[0] != cls.start_token() or lines[-1] != cls.end_token():
|
39
|
+
raise ValueError("Invalid start or end token")
|
40
|
+
content = '\n'.join(lines[1:-1])
|
41
|
+
|
42
|
+
spec = cls.parse_spec()
|
43
|
+
parsed = spec.parseString(content, parseAll=True)
|
44
|
+
return cls(**parsed.asDict())
|
45
|
+
|
46
|
+
class CodeFileModel(FormattingModel):
|
47
|
+
language: str
|
48
|
+
file_path: str
|
49
|
+
code: str
|
50
|
+
|
51
|
+
@classmethod
|
52
|
+
def format_spec(cls):
|
53
|
+
return "code_file_model\n{file_path}\n```{language}\n{code}\n```"
|
54
|
+
|
55
|
+
@classmethod
|
56
|
+
def parse_spec(cls):
|
57
|
+
header = Suppress("code_file_model") + LineEnd()
|
58
|
+
file_path = SkipTo(LineEnd()).setResultsName("file_path")
|
59
|
+
language = Suppress("```") + Word(alphas).setResultsName("language") + LineEnd()
|
60
|
+
code = SkipTo("```").setResultsName("code").setParseAction(lambda s, l, t: t[0].rstrip())
|
61
|
+
|
62
|
+
return (
|
63
|
+
header +
|
64
|
+
file_path +
|
65
|
+
language +
|
66
|
+
code +
|
67
|
+
Suppress("```")
|
68
|
+
)
|
69
|
+
|
70
|
+
@classmethod
|
71
|
+
def start_token(cls):
|
72
|
+
return '<format>'
|
73
|
+
|
74
|
+
@classmethod
|
75
|
+
def end_token(cls):
|
76
|
+
return '</format>'
|
77
|
+
|
78
|
+
# Test cases
|
79
|
+
if __name__ == "__main__":
|
80
|
+
# Test formatting
|
81
|
+
code_file = CodeFileModel(
|
82
|
+
language="Python",
|
83
|
+
file_path="src/main.py",
|
84
|
+
code="def hello():\n print('Hello, World!')"
|
85
|
+
)
|
86
|
+
formatted = CodeFileModel.format(code_file)
|
87
|
+
expected_format = """<format>
|
88
|
+
code_file_model
|
89
|
+
src/main.py
|
90
|
+
```Python
|
91
|
+
def hello():
|
92
|
+
print('Hello, World!')
|
93
|
+
```
|
94
|
+
</format>"""
|
95
|
+
assert formatted == expected_format, f"Formatting failed. Expected:\n{expected_format}\nGot:\n{formatted}"
|
96
|
+
print("Formatting test passed.")
|
97
|
+
|
98
|
+
# Test parsing
|
99
|
+
parsed = CodeFileModel.parse(formatted)
|
100
|
+
assert parsed == code_file, f"Parsing failed. Expected:\n{code_file}\nGot:\n{parsed}"
|
101
|
+
print("Parsing test passed.")
|
102
|
+
|
103
|
+
# Test round-trip
|
104
|
+
round_trip = CodeFileModel.parse(CodeFileModel.format(code_file))
|
105
|
+
assert round_trip == code_file, f"Round-trip failed. Expected:\n{code_file}\nGot:\n{round_trip}"
|
106
|
+
print("Round-trip test passed.")
|
107
|
+
|
108
|
+
# Test with different values
|
109
|
+
code_file2 = CodeFileModel(
|
110
|
+
language="JavaScript",
|
111
|
+
file_path="src/app.js",
|
112
|
+
code="function greet() {\n console.log('Hello, World!');\n}"
|
113
|
+
)
|
114
|
+
formatted2 = CodeFileModel.format(code_file2)
|
115
|
+
parsed2 = CodeFileModel.parse(formatted2)
|
116
|
+
assert parsed2 == code_file2, f"Parsing failed for different values. Expected:\n{code_file2}\nGot:\n{parsed2}"
|
117
|
+
print("Different values test passed.")
|
118
|
+
|
119
|
+
print("All tests passed successfully!")
|
120
|
+
|
121
|
+
|