Lukynnnn commited on
Commit
a55de76
·
verified ·
1 Parent(s): 1793501

Delete formatters/llm.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. formatters/llm.py +0 -262
formatters/llm.py DELETED
@@ -1,262 +0,0 @@
1
- """LLM-friendly output formatting for database results."""
2
-
3
- from dataclasses import dataclass
4
- from mcp_database_universal.config import DatabaseConfig
5
- from mcp_database_universal.engines.base import DBInfo, TableInfo, TableDetail, TableStats, QueryResult
6
-
7
-
8
- TYPE_TRANSLATIONS = {
9
- "INTEGER": "integer",
10
- "INT": "integer",
11
- "BIGINT": "large integer",
12
- "SMALLINT": "small integer",
13
- "TINYINT": "tiny integer",
14
- "REAL": "decimal number",
15
- "FLOAT": "decimal number",
16
- "DOUBLE": "decimal number",
17
- "DOUBLE PRECISION": "decimal number",
18
- "NUMERIC": "precise decimal",
19
- "DECIMAL": "precise decimal",
20
- "TEXT": "text",
21
- "VARCHAR": "text",
22
- "CHAR": "text",
23
- "BOOLEAN": "true/false",
24
- "BOOL": "true/false",
25
- "DATETIME": "date and time",
26
- "DATE": "date only",
27
- "TIMESTAMP": "date and time",
28
- "TIMESTAMP WITH TIME ZONE": "date and time (timezone)",
29
- "TIMESTAMP WITHOUT TIME ZONE": "date and time",
30
- "BLOB": "binary data",
31
- "BYTEA": "binary data",
32
- "JSON": "JSON data",
33
- "JSONB": "JSON data (optimized)",
34
- "UUID": "unique identifier",
35
- "ARRAY": "list of values",
36
- "SERIAL": "auto-increment integer",
37
- "BIGSERIAL": "auto-increment large integer",
38
- }
39
-
40
-
41
- class LLMFormatter:
42
- def __init__(self, config: DatabaseConfig):
43
- self.config = config
44
-
45
- def translate_type(self, raw_type: str) -> str:
46
- upper = raw_type.upper().strip()
47
- if "(" in upper:
48
- base = upper.split("(")[0].strip()
49
- size = upper.split("(")[1].rstrip(")")
50
- translated = TYPE_TRANSLATIONS.get(base, base.lower())
51
- return f"{translated} (max {size} chars)" if "text" in translated else translated
52
- return TYPE_TRANSLATIONS.get(upper, raw_type.lower())
53
-
54
- def format_number(self, n) -> str:
55
- if n is None:
56
- return "(empty)"
57
- if isinstance(n, float):
58
- return f"{n:,.2f}"
59
- if isinstance(n, int):
60
- return f"{n:,}"
61
- return str(n)
62
-
63
- def format_value(self, v) -> str:
64
- if v is None:
65
- return "(empty)"
66
- if isinstance(v, float):
67
- return f"{v:,.2f}"
68
- if isinstance(v, int):
69
- return f"{v:,}"
70
- return str(v)
71
-
72
- def truncate_for_llm(self, text: str) -> tuple[str, bool]:
73
- max_bytes = self.config.max_output_bytes
74
- if len(text.encode("utf-8")) <= max_bytes:
75
- return text, False
76
- lines = text.split("\n")
77
- truncated_lines = []
78
- byte_count = 0
79
- for line in lines:
80
- line_bytes = len(line.encode("utf-8")) + 1
81
- if byte_count + line_bytes > max_bytes - 200:
82
- truncated_lines.append("... (output truncated)")
83
- break
84
- truncated_lines.append(line)
85
- byte_count += line_bytes
86
- return "\n".join(truncated_lines), True
87
-
88
- def format_db_info(self, info: DBInfo) -> str:
89
- lines = [
90
- f"## Database Connection OK",
91
- f"- **Engine:** {info.engine}",
92
- f"- **Version:** {info.version}",
93
- f"- **Name:** {info.name}",
94
- f"- **Size:** {info.size_approx}",
95
- ]
96
- text = "\n".join(lines)
97
- text, _ = self.truncate_for_llm(text)
98
- return text
99
-
100
- def format_table_list(self, tables: list[TableInfo]) -> str:
101
- if not tables:
102
- return "## Tables\nNo tables found in this database."
103
-
104
- lines = [
105
- f"## Tables ({len(tables)} total)",
106
- "",
107
- "| Table | Rows | Columns | FK In | FK Out |",
108
- "|-------|------|---------|-------|--------|",
109
- ]
110
-
111
- for t in tables:
112
- lines.append(
113
- f"| {t.name} | {self.format_number(t.row_count)} | "
114
- f"{t.column_count} | {t.foreign_keys_in} | {t.foreign_keys_out} |"
115
- )
116
-
117
- text = "\n".join(lines)
118
- text, _ = self.truncate_for_llm(text)
119
- return text
120
-
121
- def format_table_detail(self, detail: TableDetail) -> str:
122
- lines = [
123
- f"## Table: {detail.name}",
124
- "",
125
- "### Columns",
126
- "| # | Name | Type | Nullable | Key |",
127
- "|---|------|------|----------|-----|",
128
- ]
129
-
130
- for i, col in enumerate(detail.columns, 1):
131
- key = ""
132
- if col.is_primary_key:
133
- key = "PK"
134
- elif col.is_foreign_key:
135
- key = f"FK -> {col.foreign_key_table}.{col.foreign_key_column}"
136
- lines.append(
137
- f"| {i} | {col.name} | {self.translate_type(col.type)} | "
138
- f"{'yes' if col.nullable else 'no'} | {key} |"
139
- )
140
-
141
- if detail.foreign_keys:
142
- lines.extend(["", "### Relationships"])
143
- for fk in detail.foreign_keys:
144
- lines.append(
145
- f"- `{detail.name}.{fk.column}` -> "
146
- f"`{fk.references_table}.{fk.references_column}`"
147
- )
148
-
149
- if detail.indexes:
150
- lines.extend(["", "### Indexes"])
151
- for idx in detail.indexes:
152
- unique = " UNIQUE" if idx.unique else ""
153
- lines.append(f"- `{idx.name}` ON ({', '.join(idx.columns)}){unique}")
154
-
155
- if detail.sample_data:
156
- lines.extend(["", "### Sample Data (first 5 rows)"])
157
- if detail.sample_data:
158
- headers = list(detail.sample_data[0].keys())
159
- lines.append("| " + " | ".join(headers) + " |")
160
- lines.append("| " + " | ".join(["---"] * len(headers)) + " |")
161
- for row in detail.sample_data:
162
- vals = [self.format_value(row.get(h)) for h in headers]
163
- lines.append("| " + " | ".join(vals) + " |")
164
-
165
- if detail.stats:
166
- lines.extend(["", "### Statistics"])
167
- lines.append(f"- **Row count:** {self.format_number(detail.stats.row_count)}")
168
- lines.append(f"- **Avg row size:** {detail.stats.avg_row_size}")
169
- lines.append(f"- **Total size:** {detail.stats.total_size}")
170
-
171
- if detail.stats.null_counts:
172
- non_zero = {k: v for k, v in detail.stats.null_counts.items() if v > 0}
173
- if non_zero:
174
- lines.append("- **NULL counts:**")
175
- for col, count in non_zero.items():
176
- lines.append(f" - {col}: {self.format_number(count)}")
177
-
178
- text = "\n".join(lines)
179
- text, _ = self.truncate_for_llm(text)
180
- return text
181
-
182
- def format_query_result(self, result: QueryResult) -> str:
183
- lines = [
184
- "## Query Results",
185
- f"**SQL:** `{result.sql}`",
186
- f"**Rows:** {result.row_count}{' (truncated)' if result.truncated else ''}",
187
- f"**Time:** {result.execution_time_ms}ms",
188
- ]
189
-
190
- if result.warning:
191
- lines.append(f"**Warning:** {result.warning}")
192
-
193
- if result.columns and result.rows:
194
- lines.append("")
195
- lines.append("| " + " | ".join(result.columns) + " |")
196
- lines.append("| " + " | ".join(["---"] * len(result.columns)) + " |")
197
- for row in result.rows:
198
- vals = [self.format_value(row.get(c)) for c in result.columns]
199
- lines.append("| " + " | ".join(vals) + " |")
200
- elif not result.rows:
201
- lines.append("\n*No rows returned.*")
202
-
203
- text = "\n".join(lines)
204
- text, _ = self.truncate_for_llm(text)
205
- return text
206
-
207
- def format_profile(self, profile_data: dict) -> str:
208
- lines = ["## Database Profile", ""]
209
-
210
- if "db_info" in profile_data:
211
- info = profile_data["db_info"]
212
- lines.extend([
213
- f"**Engine:** {info.engine} {info.version}",
214
- f"**Size:** {info.size_approx}",
215
- f"**Tables:** {profile_data.get('table_count', 'unknown')}",
216
- ])
217
-
218
- if "tables" in profile_data:
219
- lines.extend(["", "### Overview", "| Table | Rows | Columns | FK In | FK Out |", "|-------|------|---------|-------|--------|"])
220
- for t in profile_data["tables"]:
221
- lines.append(
222
- f"| {t.name} | {self.format_number(t.row_count)} | "
223
- f"{t.column_count} | {t.foreign_keys_in} | {t.foreign_keys_out} |"
224
- )
225
-
226
- if "relationships" in profile_data and profile_data["relationships"]:
227
- lines.extend(["", f"### Relationships ({len(profile_data['relationships'])})"])
228
- for rel in profile_data["relationships"]:
229
- explicit = "explicit" if rel.is_explicit else "inferred"
230
- lines.append(f"- {rel.from_table}.{rel.from_column} -> {rel.to_table}.{rel.to_column} ({rel.cardinality}, {explicit})")
231
-
232
- if "junction_tables" in profile_data and profile_data["junction_tables"]:
233
- lines.extend(["", "### Junction Tables (many-to-many)"])
234
- for jt in profile_data["junction_tables"]:
235
- lines.append(f"- `{jt}`")
236
-
237
- if "table_stats" in profile_data:
238
- lines.extend(["", "### Table Statistics"])
239
- for table_name, stats in profile_data["table_stats"].items():
240
- lines.append(f"\n#### {table_name}")
241
- lines.append(f"- Rows: {self.format_number(stats.row_count)}")
242
- lines.append(f"- Size: {stats.total_size}")
243
- if stats.null_counts:
244
- non_zero = {k: v for k, v in stats.null_counts.items() if v > 0}
245
- if non_zero:
246
- lines.append("- NULL columns:")
247
- for col, count in non_zero.items():
248
- lines.append(f" - {col}: {self.format_number(count)}")
249
-
250
- text = "\n".join(lines)
251
- text, _ = self.truncate_for_llm(text)
252
- return text
253
-
254
- def format_schema_graph(self, mermaid: str) -> str:
255
- lines = [
256
- "## Schema Graph",
257
- "",
258
- "```mermaid",
259
- mermaid,
260
- "```",
261
- ]
262
- return "\n".join(lines)