pychartai 0.5.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.
- pychartai/__init__.py +504 -0
- pychartai/cli.py +91 -0
- pychartai-0.5.0.dist-info/METADATA +240 -0
- pychartai-0.5.0.dist-info/RECORD +49 -0
- pychartai-0.5.0.dist-info/WHEEL +5 -0
- pychartai-0.5.0.dist-info/entry_points.txt +4 -0
- pychartai-0.5.0.dist-info/licenses/LICENSE +21 -0
- pychartai-0.5.0.dist-info/top_level.txt +3 -0
- pychartai_core/__init__.py +148 -0
- pychartai_core/agent.py +1248 -0
- pychartai_core/api.py +189 -0
- pychartai_core/cache.py +119 -0
- pychartai_core/cloud_connectors.py +364 -0
- pychartai_core/code_sanitizer.py +144 -0
- pychartai_core/config.py +115 -0
- pychartai_core/connections.py +253 -0
- pychartai_core/dashboard_report.py +266 -0
- pychartai_core/data_manager.py +116 -0
- pychartai_core/db_connectors.py +302 -0
- pychartai_core/error_hints.py +79 -0
- pychartai_core/intent_classifier.py +203 -0
- pychartai_core/io.py +86 -0
- pychartai_core/logging.py +103 -0
- pychartai_core/memory.py +134 -0
- pychartai_core/model_config.py +404 -0
- pychartai_core/model_manager.py +209 -0
- pychartai_core/pipeline.py +334 -0
- pychartai_core/profiler.py +171 -0
- pychartai_core/providers.py +402 -0
- pychartai_core/redactor.py +172 -0
- pychartai_core/reporter.py +466 -0
- pychartai_core/sandbox.py +865 -0
- pychartai_core/schema.py +100 -0
- pychartai_core/skills.py +179 -0
- pychartai_core/smart_df.py +635 -0
- pychartai_core/streaming.py +56 -0
- pychartai_core/themes.py +170 -0
- pychartai_core/visualization.py +391 -0
- pychartai_core/visualization_backends/__init__.py +29 -0
- pychartai_core/visualization_backends/base.py +85 -0
- pychartai_core/visualization_backends/catalog.py +172 -0
- pychartai_core/visualization_backends/matplotlib_backend.py +700 -0
- pychartai_core/visualization_backends/plotly_backend.py +439 -0
- pychartai_core/visualization_backends/seaborn_backend.py +839 -0
- pychartai_core/visualization_backends/utils.py +95 -0
- pychartai_mcp/__init__.py +1 -0
- pychartai_mcp/agent_server.py +78 -0
- pychartai_mcp/server.py +97 -0
- pychartai_mcp/tools.py +251 -0
pychartai/__init__.py
ADDED
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
"""
|
|
2
|
+
pychartai — AI-powered data analysis and chart generation.
|
|
3
|
+
|
|
4
|
+
Single public API. Everything a user needs is available directly under
|
|
5
|
+
``import pychartai as pai``. The ``pychartai_core`` sub-package is an
|
|
6
|
+
implementation detail and should not be imported directly.
|
|
7
|
+
|
|
8
|
+
Quick start::
|
|
9
|
+
|
|
10
|
+
import pychartai as pai
|
|
11
|
+
|
|
12
|
+
llm = pai.OllamaLLM(model='llama3.2')
|
|
13
|
+
pai.config.set({'llm': llm})
|
|
14
|
+
|
|
15
|
+
df = pai.read_csv('data/sales.csv')
|
|
16
|
+
print(df.chat('What is the average revenue by region?'))
|
|
17
|
+
df.chat('Plot a bar chart of revenue by region', chart_library='plotly')
|
|
18
|
+
|
|
19
|
+
# Data profiling — no LLM needed
|
|
20
|
+
report = df.profile()
|
|
21
|
+
print(report.summary)
|
|
22
|
+
|
|
23
|
+
# Conversation memory
|
|
24
|
+
df.enable_memory()
|
|
25
|
+
df.chat('Total sales by region')
|
|
26
|
+
df.chat('Now show that as a percentage')
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
import sys
|
|
32
|
+
from typing import Any, Optional
|
|
33
|
+
import pandas as pd
|
|
34
|
+
|
|
35
|
+
# ---------------------------------------------------------------------------
|
|
36
|
+
# Core imports from implementation package
|
|
37
|
+
# ---------------------------------------------------------------------------
|
|
38
|
+
|
|
39
|
+
from pychartai_core import config as global_config
|
|
40
|
+
from pychartai_core.providers import (
|
|
41
|
+
PyChartLLM,
|
|
42
|
+
OllamaLLM,
|
|
43
|
+
GitHubLLM,
|
|
44
|
+
OpenAILLM,
|
|
45
|
+
GeminiLLM,
|
|
46
|
+
AnthropicLLM,
|
|
47
|
+
QwenLLM,
|
|
48
|
+
DeepSeekLLM,
|
|
49
|
+
GenericLLM,
|
|
50
|
+
)
|
|
51
|
+
from pychartai_core.smart_df import SmartDataFrame
|
|
52
|
+
from pychartai_core.agent import PyChartAgent, TransformationLog, QueryIntent
|
|
53
|
+
from pychartai_core.sandbox import RestrictedSandbox, DockerSandbox
|
|
54
|
+
from pychartai_core.streaming import StreamEvent
|
|
55
|
+
from pychartai_core.reporter import InsightReporter
|
|
56
|
+
from pychartai_core.skills import Skill, skill
|
|
57
|
+
from pychartai_core.schema import Schema, Column
|
|
58
|
+
from pychartai_core.cache import ResponseCache
|
|
59
|
+
from pychartai_core.memory import ConversationMemory, Turn
|
|
60
|
+
from pychartai_core.profiler import DataProfiler, ProfileReport
|
|
61
|
+
from pychartai_core.themes import ChartTheme, resolve_theme, BUILTIN_THEMES
|
|
62
|
+
from pychartai_core.error_hints import get_hint, format_error_with_hint
|
|
63
|
+
from pychartai_core.model_config import init, load_model_config, get_model, list_models
|
|
64
|
+
from pychartai_core.redactor import DataRedactor
|
|
65
|
+
from pychartai_core.logging import (
|
|
66
|
+
get_logger, configure_logger, set_log_level,
|
|
67
|
+
)
|
|
68
|
+
from pychartai_core.pipeline import (
|
|
69
|
+
Pipeline, PipelineStep, PipelineContext,
|
|
70
|
+
ValidateInput, InjectSchema, InjectSkills,
|
|
71
|
+
CacheLookup, CallAnalyzer, CallOwnAgent, CacheStore,
|
|
72
|
+
default_pipeline,
|
|
73
|
+
)
|
|
74
|
+
from pychartai_core.connections import (
|
|
75
|
+
BaseConnection, CSVConnection, ExcelConnection,
|
|
76
|
+
JSONConnection, ParquetConnection, SQLConnection,
|
|
77
|
+
connect,
|
|
78
|
+
)
|
|
79
|
+
from pychartai_core.db_connectors import (
|
|
80
|
+
PostgreSQLConnection,
|
|
81
|
+
MySQLConnection,
|
|
82
|
+
SnowflakeConnection,
|
|
83
|
+
BigQueryConnection,
|
|
84
|
+
RedshiftConnection,
|
|
85
|
+
)
|
|
86
|
+
from pychartai_core.cloud_connectors import (
|
|
87
|
+
S3Connection,
|
|
88
|
+
GCSConnection,
|
|
89
|
+
AzureBlobConnection,
|
|
90
|
+
GoogleSheetsConnection,
|
|
91
|
+
)
|
|
92
|
+
from pychartai_core.visualization import (
|
|
93
|
+
get_chart_helper_names,
|
|
94
|
+
list_backends,
|
|
95
|
+
area_chart,
|
|
96
|
+
bar_chart,
|
|
97
|
+
box_chart,
|
|
98
|
+
bubble_chart,
|
|
99
|
+
count_chart,
|
|
100
|
+
ecdf_chart,
|
|
101
|
+
funnel_chart,
|
|
102
|
+
heatmap,
|
|
103
|
+
histogram,
|
|
104
|
+
kde_chart,
|
|
105
|
+
line_chart,
|
|
106
|
+
pairplot_chart,
|
|
107
|
+
pie_chart,
|
|
108
|
+
regression_chart,
|
|
109
|
+
scatter_chart,
|
|
110
|
+
stacked_bar_chart,
|
|
111
|
+
step_chart,
|
|
112
|
+
strip_chart,
|
|
113
|
+
swarm_chart,
|
|
114
|
+
violin_chart,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
__version__ = '0.5.0'
|
|
120
|
+
|
|
121
|
+
# Expose global config singleton under the expected name
|
|
122
|
+
config = global_config
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
# ---------------------------------------------------------------------------
|
|
126
|
+
# Convenience helpers
|
|
127
|
+
# ---------------------------------------------------------------------------
|
|
128
|
+
|
|
129
|
+
def available_backends() -> tuple[str, ...]:
|
|
130
|
+
"""Return the names of all supported chart backends."""
|
|
131
|
+
return tuple(list_backends())
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def available_charts(chart_library: Optional[str] = None) -> tuple[str, ...]:
|
|
135
|
+
"""Return all supported chart helper names, optionally filtered by backend."""
|
|
136
|
+
return get_chart_helper_names(chart_library)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
# ---------------------------------------------------------------------------
|
|
140
|
+
# SmartDataFrame alias — canonical class is SmartDataFrame (capital F).
|
|
141
|
+
# SmartDataframe (lower-case f) is kept as a backward-compatible alias.
|
|
142
|
+
# ---------------------------------------------------------------------------
|
|
143
|
+
|
|
144
|
+
# SmartDataFrame is already imported above; re-export the alias
|
|
145
|
+
SmartDataframe = SmartDataFrame # backward-compat alias
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
# ---------------------------------------------------------------------------
|
|
149
|
+
# I/O helpers — return SmartDataFrame directly
|
|
150
|
+
# ---------------------------------------------------------------------------
|
|
151
|
+
|
|
152
|
+
def DataFrame(
|
|
153
|
+
data: Any = None,
|
|
154
|
+
*,
|
|
155
|
+
config: Optional[dict[str, Any]] = None,
|
|
156
|
+
chart_library: Optional[str] = None,
|
|
157
|
+
**kwargs: Any,
|
|
158
|
+
) -> SmartDataFrame:
|
|
159
|
+
"""Create a :class:`SmartDataFrame` from any pandas-compatible input.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
data: Data passed through to :class:`pandas.DataFrame`.
|
|
163
|
+
config: Optional dict of pychartai config overrides to apply.
|
|
164
|
+
chart_library: Default chart backend for this instance.
|
|
165
|
+
**kwargs: Forwarded to :class:`pandas.DataFrame`.
|
|
166
|
+
"""
|
|
167
|
+
if isinstance(data, pd.DataFrame):
|
|
168
|
+
raw = data
|
|
169
|
+
elif isinstance(data, SmartDataFrame):
|
|
170
|
+
raw = data._df
|
|
171
|
+
else:
|
|
172
|
+
raw = pd.DataFrame(data, **kwargs)
|
|
173
|
+
sdf = SmartDataFrame(raw)
|
|
174
|
+
if chart_library:
|
|
175
|
+
global_config.set({'chart_backend': chart_library})
|
|
176
|
+
if config:
|
|
177
|
+
normalized = dict(config)
|
|
178
|
+
if 'chart_library' in normalized and 'chart_backend' not in normalized:
|
|
179
|
+
normalized['chart_backend'] = normalized.pop('chart_library')
|
|
180
|
+
global_config.set(normalized)
|
|
181
|
+
return sdf
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def read_csv(
|
|
185
|
+
filepath_or_buffer: Any,
|
|
186
|
+
*,
|
|
187
|
+
config: Optional[dict[str, Any]] = None,
|
|
188
|
+
chart_library: Optional[str] = None,
|
|
189
|
+
**kwargs: Any,
|
|
190
|
+
) -> SmartDataFrame:
|
|
191
|
+
"""Read a CSV file and return a :class:`SmartDataFrame`."""
|
|
192
|
+
return DataFrame(pd.read_csv(filepath_or_buffer, **kwargs),
|
|
193
|
+
config=config, chart_library=chart_library)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def read_excel(
|
|
197
|
+
filepath_or_buffer: Any,
|
|
198
|
+
*,
|
|
199
|
+
config: Optional[dict[str, Any]] = None,
|
|
200
|
+
chart_library: Optional[str] = None,
|
|
201
|
+
**kwargs: Any,
|
|
202
|
+
) -> SmartDataFrame:
|
|
203
|
+
"""Read an Excel file and return a :class:`SmartDataFrame`."""
|
|
204
|
+
return DataFrame(pd.read_excel(filepath_or_buffer, **kwargs),
|
|
205
|
+
config=config, chart_library=chart_library)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def read_json(
|
|
209
|
+
filepath_or_buffer: Any,
|
|
210
|
+
*,
|
|
211
|
+
config: Optional[dict[str, Any]] = None,
|
|
212
|
+
chart_library: Optional[str] = None,
|
|
213
|
+
**kwargs: Any,
|
|
214
|
+
) -> SmartDataFrame:
|
|
215
|
+
"""Read a JSON file and return a :class:`SmartDataFrame`."""
|
|
216
|
+
return DataFrame(pd.read_json(filepath_or_buffer, **kwargs),
|
|
217
|
+
config=config, chart_library=chart_library)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def read_parquet(
|
|
221
|
+
filepath_or_buffer: Any,
|
|
222
|
+
*,
|
|
223
|
+
config: Optional[dict[str, Any]] = None,
|
|
224
|
+
chart_library: Optional[str] = None,
|
|
225
|
+
**kwargs: Any,
|
|
226
|
+
) -> SmartDataFrame:
|
|
227
|
+
"""Read a Parquet file and return a :class:`SmartDataFrame`."""
|
|
228
|
+
return DataFrame(pd.read_parquet(filepath_or_buffer, **kwargs),
|
|
229
|
+
config=config, chart_library=chart_library)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
# ---------------------------------------------------------------------------
|
|
233
|
+
# Module-level chat() — multi-DataFrame queries
|
|
234
|
+
# ---------------------------------------------------------------------------
|
|
235
|
+
|
|
236
|
+
def _normalize_df(value: Any) -> pd.DataFrame:
|
|
237
|
+
"""Coerce a SmartDataFrame or plain DataFrame to pd.DataFrame."""
|
|
238
|
+
if isinstance(value, SmartDataFrame):
|
|
239
|
+
return object.__getattribute__(value, '_df')
|
|
240
|
+
if isinstance(value, pd.DataFrame):
|
|
241
|
+
return value
|
|
242
|
+
raise TypeError(
|
|
243
|
+
f'pychartai.chat() expects pandas DataFrame or SmartDataFrame, got {type(value).__name__}'
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def chat(
|
|
249
|
+
query: str,
|
|
250
|
+
*dataframes: Any,
|
|
251
|
+
sandbox: Optional[Any] = None,
|
|
252
|
+
chart_library: Optional[str] = None,
|
|
253
|
+
plot_type: Optional[str] = None,
|
|
254
|
+
chart_options: Optional[dict[str, Any]] = None,
|
|
255
|
+
model: Optional[Any] = None,
|
|
256
|
+
**chart_kwargs: Any,
|
|
257
|
+
) -> str:
|
|
258
|
+
"""Run a natural-language query over one or more DataFrames.
|
|
259
|
+
|
|
260
|
+
Call ``.chat()`` directly on a :class:`SmartDataFrame` (recommended)::
|
|
261
|
+
|
|
262
|
+
df = pai.read_csv('data.csv')
|
|
263
|
+
df.chat('Who earns the most?')
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
query: Natural-language question.
|
|
267
|
+
*dataframes: One or more :class:`pandas.DataFrame` or
|
|
268
|
+
:class:`SmartDataFrame` objects.
|
|
269
|
+
sandbox: Optional :class:`RestrictedSandbox` or
|
|
270
|
+
:class:`DockerSandbox`.
|
|
271
|
+
chart_library: Override the visualization backend
|
|
272
|
+
('seaborn', 'matplotlib', 'plotly').
|
|
273
|
+
plot_type: Chart type hint (e.g. 'bar', 'line').
|
|
274
|
+
chart_options: Extra chart keyword options.
|
|
275
|
+
model: Optional model override. Can be a preset name,
|
|
276
|
+
``"provider/model"`` string, or a ``PyChartLLM`` instance.
|
|
277
|
+
Falls back to global config if omitted.
|
|
278
|
+
**chart_kwargs: Additional chart parameters.
|
|
279
|
+
|
|
280
|
+
Returns:
|
|
281
|
+
Analysis result as a string, or a chart file path.
|
|
282
|
+
|
|
283
|
+
Raises:
|
|
284
|
+
ValueError: If no DataFrames are passed.
|
|
285
|
+
RuntimeError: If no LLM is configured.
|
|
286
|
+
|
|
287
|
+
"""
|
|
288
|
+
if not dataframes:
|
|
289
|
+
raise ValueError('pychartai.chat() requires at least one DataFrame.')
|
|
290
|
+
|
|
291
|
+
# Resolve model: explicit > global config
|
|
292
|
+
llm = None
|
|
293
|
+
if model is not None:
|
|
294
|
+
if isinstance(model, (OllamaLLM, GitHubLLM, OpenAILLM, GeminiLLM,
|
|
295
|
+
AnthropicLLM, QwenLLM, DeepSeekLLM, GenericLLM)):
|
|
296
|
+
llm = model
|
|
297
|
+
elif isinstance(model, str):
|
|
298
|
+
from pychartai_core.model_config import _MODEL_REGISTRY, get_model, _build_from_provider
|
|
299
|
+
if model in _MODEL_REGISTRY:
|
|
300
|
+
llm = get_model(model)
|
|
301
|
+
elif '/' in model:
|
|
302
|
+
prov, mname = model.split('/', 1)
|
|
303
|
+
llm = _build_from_provider(prov, mname)
|
|
304
|
+
else:
|
|
305
|
+
raise KeyError(
|
|
306
|
+
f"Unknown model {model!r}. Use a preset name from models.yml, "
|
|
307
|
+
"'provider/model' format, or a PyChartLLM instance."
|
|
308
|
+
)
|
|
309
|
+
else:
|
|
310
|
+
raise TypeError(f"model must be str or PyChartLLM, got {type(model).__name__}")
|
|
311
|
+
else:
|
|
312
|
+
llm = config.get('llm')
|
|
313
|
+
|
|
314
|
+
if llm is None:
|
|
315
|
+
raise RuntimeError(
|
|
316
|
+
'No LLM configured. '
|
|
317
|
+
"Use pai.init() at startup, e.g.: pai.init(model='deepseek-v4-flash')"
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
backend = chart_library or config.get('chart_backend', 'seaborn')
|
|
321
|
+
output_dir = config.get('charts_output_dir', 'exports/charts')
|
|
322
|
+
directive = SmartDataFrame._build_chart_directive(plot_type, chart_options, chart_kwargs)
|
|
323
|
+
effective_query = query + directive
|
|
324
|
+
frames = [_normalize_df(item) for item in dataframes]
|
|
325
|
+
|
|
326
|
+
if sandbox is not None:
|
|
327
|
+
from pychartai_core.agent import PyChartAgent
|
|
328
|
+
agent = PyChartAgent(llm=llm, chart_backend=backend, charts_output_dir=output_dir)
|
|
329
|
+
if len(frames) == 1:
|
|
330
|
+
return agent.chat(effective_query, frames[0])
|
|
331
|
+
# Multi-DataFrame: merge and query
|
|
332
|
+
import pandas as _pd
|
|
333
|
+
merged = _pd.concat(frames, ignore_index=True)
|
|
334
|
+
return agent.chat(effective_query, merged)
|
|
335
|
+
|
|
336
|
+
# Default path: use PyChartAgent
|
|
337
|
+
from pychartai_core.agent import PyChartAgent
|
|
338
|
+
agent = PyChartAgent(llm=llm, chart_backend=backend, charts_output_dir=output_dir)
|
|
339
|
+
if len(frames) == 1:
|
|
340
|
+
return agent.chat(effective_query, frames[0])
|
|
341
|
+
import pandas as _pd
|
|
342
|
+
merged = _pd.concat(frames, ignore_index=True)
|
|
343
|
+
return agent.chat(effective_query, merged)
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
async def achat(
|
|
347
|
+
query: str,
|
|
348
|
+
*dataframes: Any,
|
|
349
|
+
sandbox: Optional[Any] = None,
|
|
350
|
+
chart_library: Optional[str] = None,
|
|
351
|
+
plot_type: Optional[str] = None,
|
|
352
|
+
chart_options: Optional[dict[str, Any]] = None,
|
|
353
|
+
**chart_kwargs: Any,
|
|
354
|
+
) -> str:
|
|
355
|
+
"""Async wrapper around :func:`chat`.
|
|
356
|
+
|
|
357
|
+
Runs the synchronous call in a thread-pool executor so it can be awaited
|
|
358
|
+
without blocking the event loop::
|
|
359
|
+
|
|
360
|
+
result = await pai.achat('Who earns the most?', df)
|
|
361
|
+
"""
|
|
362
|
+
import asyncio
|
|
363
|
+
loop = asyncio.get_event_loop()
|
|
364
|
+
return await loop.run_in_executor(
|
|
365
|
+
None,
|
|
366
|
+
lambda: chat(
|
|
367
|
+
query,
|
|
368
|
+
*dataframes,
|
|
369
|
+
sandbox=sandbox,
|
|
370
|
+
chart_library=chart_library,
|
|
371
|
+
plot_type=plot_type,
|
|
372
|
+
chart_options=chart_options,
|
|
373
|
+
**chart_kwargs,
|
|
374
|
+
),
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
# ---------------------------------------------------------------------------
|
|
379
|
+
# Public API surface
|
|
380
|
+
# ---------------------------------------------------------------------------
|
|
381
|
+
|
|
382
|
+
__all__ = [
|
|
383
|
+
'__version__',
|
|
384
|
+
# Config
|
|
385
|
+
'config',
|
|
386
|
+
# LLM providers
|
|
387
|
+
'PyChartLLM',
|
|
388
|
+
'OllamaLLM',
|
|
389
|
+
'GitHubLLM',
|
|
390
|
+
'OpenAILLM',
|
|
391
|
+
'GeminiLLM',
|
|
392
|
+
'AnthropicLLM',
|
|
393
|
+
'QwenLLM',
|
|
394
|
+
'DeepSeekLLM',
|
|
395
|
+
'GenericLLM',
|
|
396
|
+
|
|
397
|
+
# DataFrame
|
|
398
|
+
'SmartDataFrame',
|
|
399
|
+
'SmartDataframe', # backward-compat alias
|
|
400
|
+
'DataFrame',
|
|
401
|
+
'read_csv',
|
|
402
|
+
'read_excel',
|
|
403
|
+
'read_json',
|
|
404
|
+
'read_parquet',
|
|
405
|
+
# Agents
|
|
406
|
+
'PyChartAgent',
|
|
407
|
+
'TransformationLog',
|
|
408
|
+
'QueryIntent',
|
|
409
|
+
# Module-level chat
|
|
410
|
+
'chat',
|
|
411
|
+
'achat',
|
|
412
|
+
# Sandbox & streaming
|
|
413
|
+
'RestrictedSandbox',
|
|
414
|
+
'DockerSandbox',
|
|
415
|
+
'StreamEvent',
|
|
416
|
+
# Memory
|
|
417
|
+
'ConversationMemory',
|
|
418
|
+
'Turn',
|
|
419
|
+
# Profiling
|
|
420
|
+
'DataProfiler',
|
|
421
|
+
'ProfileReport',
|
|
422
|
+
# Themes
|
|
423
|
+
'ChartTheme',
|
|
424
|
+
'resolve_theme',
|
|
425
|
+
'BUILTIN_THEMES',
|
|
426
|
+
# Skills
|
|
427
|
+
'Skill',
|
|
428
|
+
'skill',
|
|
429
|
+
# Semantic layer
|
|
430
|
+
'Schema',
|
|
431
|
+
'Column',
|
|
432
|
+
# Cache
|
|
433
|
+
'ResponseCache',
|
|
434
|
+
# PII redaction
|
|
435
|
+
'DataRedactor',
|
|
436
|
+
# Error hints
|
|
437
|
+
'get_hint',
|
|
438
|
+
'format_error_with_hint',
|
|
439
|
+
# Logging
|
|
440
|
+
'get_logger',
|
|
441
|
+
'configure_logger',
|
|
442
|
+
'set_log_level',
|
|
443
|
+
|
|
444
|
+
# Pipeline
|
|
445
|
+
'Pipeline',
|
|
446
|
+
'PipelineStep',
|
|
447
|
+
'PipelineContext',
|
|
448
|
+
'ValidateInput',
|
|
449
|
+
'InjectSchema',
|
|
450
|
+
'InjectSkills',
|
|
451
|
+
'CacheLookup',
|
|
452
|
+
'CallAnalyzer',
|
|
453
|
+
'CallOwnAgent',
|
|
454
|
+
'CacheStore',
|
|
455
|
+
'default_pipeline',
|
|
456
|
+
# Connections
|
|
457
|
+
'BaseConnection',
|
|
458
|
+
'CSVConnection',
|
|
459
|
+
'ExcelConnection',
|
|
460
|
+
'JSONConnection',
|
|
461
|
+
'ParquetConnection',
|
|
462
|
+
'SQLConnection',
|
|
463
|
+
'connect',
|
|
464
|
+
# Database connectors
|
|
465
|
+
'PostgreSQLConnection',
|
|
466
|
+
'MySQLConnection',
|
|
467
|
+
'SnowflakeConnection',
|
|
468
|
+
'BigQueryConnection',
|
|
469
|
+
'RedshiftConnection',
|
|
470
|
+
# Cloud storage connectors
|
|
471
|
+
'S3Connection',
|
|
472
|
+
'GCSConnection',
|
|
473
|
+
'AzureBlobConnection',
|
|
474
|
+
'GoogleSheetsConnection',
|
|
475
|
+
# Chart helpers (all 20)
|
|
476
|
+
'area_chart',
|
|
477
|
+
'bar_chart',
|
|
478
|
+
'box_chart',
|
|
479
|
+
'bubble_chart',
|
|
480
|
+
'count_chart',
|
|
481
|
+
'ecdf_chart',
|
|
482
|
+
'funnel_chart',
|
|
483
|
+
'heatmap',
|
|
484
|
+
'histogram',
|
|
485
|
+
'kde_chart',
|
|
486
|
+
'line_chart',
|
|
487
|
+
'pairplot_chart',
|
|
488
|
+
'pie_chart',
|
|
489
|
+
'regression_chart',
|
|
490
|
+
'scatter_chart',
|
|
491
|
+
'stacked_bar_chart',
|
|
492
|
+
'step_chart',
|
|
493
|
+
'strip_chart',
|
|
494
|
+
'swarm_chart',
|
|
495
|
+
'violin_chart',
|
|
496
|
+
# Utilities
|
|
497
|
+
'available_backends',
|
|
498
|
+
'available_charts',
|
|
499
|
+
'get_hint',
|
|
500
|
+
'format_error_with_hint',
|
|
501
|
+
# Reporting
|
|
502
|
+
'InsightReporter',
|
|
503
|
+
|
|
504
|
+
]
|
pychartai/cli.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""pychartai.cli — command-line entry point.
|
|
2
|
+
|
|
3
|
+
pychartai data.csv "What is the average revenue by region?"
|
|
4
|
+
pychartai data.csv --profile
|
|
5
|
+
pychartai data.csv --report report.html
|
|
6
|
+
pychartai data.csv "Plot revenue by region" --provider openai --model gpt-4o --backend plotly
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
import sys
|
|
13
|
+
|
|
14
|
+
import pychartai as pai
|
|
15
|
+
|
|
16
|
+
_READERS = {
|
|
17
|
+
'csv': pai.read_csv,
|
|
18
|
+
'xlsx': pai.read_excel,
|
|
19
|
+
'xls': pai.read_excel,
|
|
20
|
+
'json': pai.read_json,
|
|
21
|
+
'parquet': pai.read_parquet,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
_LLM_BUILDERS = {
|
|
25
|
+
'ollama': pai.OllamaLLM,
|
|
26
|
+
'openai': pai.OpenAILLM,
|
|
27
|
+
'github': pai.GitHubLLM,
|
|
28
|
+
'qwen': pai.QwenLLM,
|
|
29
|
+
'gemini': pai.GeminiLLM,
|
|
30
|
+
'anthropic': pai.AnthropicLLM,
|
|
31
|
+
'deepseek': pai.DeepSeekLLM,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _load(path: str):
|
|
36
|
+
ext = path.rsplit('.', 1)[-1].lower() if '.' in path else ''
|
|
37
|
+
if ext not in _READERS:
|
|
38
|
+
raise ValueError(f'Unsupported file extension {ext!r}. Supported: {sorted(_READERS)}')
|
|
39
|
+
return _READERS[ext](path)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def main(argv=None) -> int:
|
|
43
|
+
ap = argparse.ArgumentParser(prog='pychartai', description=__doc__,
|
|
44
|
+
formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
45
|
+
ap.add_argument('file', help='CSV/Excel/JSON/Parquet file to load')
|
|
46
|
+
ap.add_argument('query', nargs='?', help='Natural-language question (omit with --profile/--report)')
|
|
47
|
+
ap.add_argument('--provider', default='ollama', choices=sorted(_LLM_BUILDERS))
|
|
48
|
+
ap.add_argument('--model', default='llama3.2', help='Model name (default: llama3.2)')
|
|
49
|
+
ap.add_argument('--backend', choices=['seaborn', 'matplotlib', 'plotly'], help='Chart backend override')
|
|
50
|
+
ap.add_argument('--profile', action='store_true', help='Print an auto-EDA profile (no LLM call)')
|
|
51
|
+
ap.add_argument('--report', metavar='PATH', help='Write a self-contained HTML insight report to PATH')
|
|
52
|
+
args = ap.parse_args(argv)
|
|
53
|
+
|
|
54
|
+
# Use per-provider default model when the generic default doesn't apply
|
|
55
|
+
_DEFAULT_MODELS = {
|
|
56
|
+
'openai': 'gpt-4o',
|
|
57
|
+
'github': 'gpt-4.1',
|
|
58
|
+
'qwen': 'qwen-plus',
|
|
59
|
+
'gemini': 'gemini-2.0-flash',
|
|
60
|
+
'anthropic': 'claude-3-5-sonnet-20241022',
|
|
61
|
+
'deepseek': 'deepseek-chat',
|
|
62
|
+
}
|
|
63
|
+
if args.model == 'llama3.2' and args.provider in _DEFAULT_MODELS:
|
|
64
|
+
args.model = _DEFAULT_MODELS[args.provider]
|
|
65
|
+
|
|
66
|
+
if not args.query and not args.profile and not args.report:
|
|
67
|
+
ap.error('provide a query, or pass --profile / --report')
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
df = _load(args.file)
|
|
71
|
+
except (ValueError, OSError) as exc:
|
|
72
|
+
print(f'pychartai: error: {exc}', file=sys.stderr)
|
|
73
|
+
return 1
|
|
74
|
+
|
|
75
|
+
if args.profile:
|
|
76
|
+
print(df.profile().summary)
|
|
77
|
+
return 0
|
|
78
|
+
|
|
79
|
+
if args.report:
|
|
80
|
+
path = df.report(args.report)
|
|
81
|
+
print(f'Report written to {path}')
|
|
82
|
+
return 0
|
|
83
|
+
|
|
84
|
+
pai.config.set({'llm': _LLM_BUILDERS[args.provider](model=args.model)})
|
|
85
|
+
result = df.chat(args.query, chart_library=args.backend) if args.backend else df.chat(args.query)
|
|
86
|
+
print(result)
|
|
87
|
+
return 0
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
if __name__ == '__main__':
|
|
91
|
+
sys.exit(main())
|