From 51f261eaa3d10de63cef44257adb5b143688d0ed Mon Sep 17 00:00:00 2001 From: maddy Date: Fri, 8 Aug 2025 18:11:32 -0400 Subject: [PATCH 1/4] HelpersTask986: added schema_parser and raw_data_analyser with langgraph architecture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pre-commit checks: All checks passed ✅ --- langgraph/.codespellignore | 0 langgraph/.env.example | 4 + .../.github/workflows/integration-tests.yml | 42 ++ langgraph/.github/workflows/unit-tests.yml | 57 ++ langgraph/.gitignore | 165 +++++ langgraph/LICENSE | 21 + langgraph/Makefile | 67 ++ langgraph/README.md | 80 +++ langgraph/langgraph.json | 8 + langgraph/openaitest.py | 13 + langgraph/pyproject.toml | 65 ++ langgraph/src/agent/__init__.py | 8 + langgraph/src/agent/graph.py | 158 ++++ langgraph/src/agent/raw_data_analyzer.py | 620 ++++++++++++++++ langgraph/src/agent/schema_parser.py | 677 ++++++++++++++++++ langgraph/static/studio_ui.png | Bin 0 -> 287630 bytes langgraph/test.py | 189 +++++ langgraph/tests/conftest.py | 6 + langgraph/tests/integration_tests/__init__.py | 1 + .../tests/integration_tests/test_graph.py | 12 + langgraph/tests/unit_tests/__init__.py | 1 + .../tests/unit_tests/test_configuration.py | 9 + 22 files changed, 2203 insertions(+) create mode 100644 langgraph/.codespellignore create mode 100644 langgraph/.env.example create mode 100644 langgraph/.github/workflows/integration-tests.yml create mode 100644 langgraph/.github/workflows/unit-tests.yml create mode 100644 langgraph/.gitignore create mode 100644 langgraph/LICENSE create mode 100644 langgraph/Makefile create mode 100644 langgraph/README.md create mode 100644 langgraph/langgraph.json create mode 100644 langgraph/openaitest.py create mode 100644 langgraph/pyproject.toml create mode 100644 langgraph/src/agent/__init__.py create mode 100644 langgraph/src/agent/graph.py create mode 100644 langgraph/src/agent/raw_data_analyzer.py create mode 100755 langgraph/src/agent/schema_parser.py create mode 100644 langgraph/static/studio_ui.png create mode 100644 langgraph/test.py create mode 100644 langgraph/tests/conftest.py create mode 100644 langgraph/tests/integration_tests/__init__.py create mode 100644 langgraph/tests/integration_tests/test_graph.py create mode 100644 langgraph/tests/unit_tests/__init__.py create mode 100644 langgraph/tests/unit_tests/test_configuration.py diff --git a/langgraph/.codespellignore b/langgraph/.codespellignore new file mode 100644 index 000000000..e69de29bb diff --git a/langgraph/.env.example b/langgraph/.env.example new file mode 100644 index 000000000..ec66fc0ad --- /dev/null +++ b/langgraph/.env.example @@ -0,0 +1,4 @@ +# To separate your traces from other application +LANGSMITH_PROJECT=new-agent + +# Add API keys for connecting to LLM providers, data sources, and other integrations here diff --git a/langgraph/.github/workflows/integration-tests.yml b/langgraph/.github/workflows/integration-tests.yml new file mode 100644 index 000000000..259852d82 --- /dev/null +++ b/langgraph/.github/workflows/integration-tests.yml @@ -0,0 +1,42 @@ +# This workflow will run integration tests for the current project once per day + +name: Integration Tests + +on: + schedule: + - cron: "37 14 * * *" # Run at 7:37 AM Pacific Time (14:37 UTC) every day + workflow_dispatch: # Allows triggering the workflow manually in GitHub UI + +# If another scheduled run starts while this workflow is still running, +# cancel the earlier run in favor of the next run. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + integration-tests: + name: Integration Tests + strategy: + matrix: + os: [ubuntu-latest] + python-version: ["3.11", "3.12"] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + curl -LsSf https://astral.sh/uv/install.sh | sh + uv venv + uv pip install -r pyproject.toml + uv pip install -U pytest-asyncio + - name: Run integration tests + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + LANGSMITH_API_KEY: ${{ secrets.LANGSMITH_API_KEY }} + LANGSMITH_TRACING: true + run: | + uv run pytest tests/integration_tests diff --git a/langgraph/.github/workflows/unit-tests.yml b/langgraph/.github/workflows/unit-tests.yml new file mode 100644 index 000000000..055407c23 --- /dev/null +++ b/langgraph/.github/workflows/unit-tests.yml @@ -0,0 +1,57 @@ +# This workflow will run unit tests for the current project + +name: CI + +on: + push: + branches: ["main"] + pull_request: + workflow_dispatch: # Allows triggering the workflow manually in GitHub UI + +# If another push to the same PR or branch happens while this workflow is still running, +# cancel the earlier run in favor of the next run. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + unit-tests: + name: Unit Tests + strategy: + matrix: + os: [ubuntu-latest] + python-version: ["3.11", "3.12"] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + curl -LsSf https://astral.sh/uv/install.sh | sh + uv venv + uv pip install -r pyproject.toml + - name: Lint with ruff + run: | + uv pip install ruff + uv run ruff check . + - name: Lint with mypy + run: | + uv pip install mypy + uv run mypy --strict src/ + - name: Check README spelling + uses: codespell-project/actions-codespell@v2 + with: + ignore_words_file: .codespellignore + path: README.md + - name: Check code spelling + uses: codespell-project/actions-codespell@v2 + with: + ignore_words_file: .codespellignore + path: src/ + - name: Run tests with pytest + run: | + uv pip install pytest + uv run pytest tests/unit_tests diff --git a/langgraph/.gitignore b/langgraph/.gitignore new file mode 100644 index 000000000..eb170e827 --- /dev/null +++ b/langgraph/.gitignore @@ -0,0 +1,165 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +venv.langgraph/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ +uv.lock +.langgraph_api/ diff --git a/langgraph/LICENSE b/langgraph/LICENSE new file mode 100644 index 000000000..57d0481d4 --- /dev/null +++ b/langgraph/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 LangChain + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/langgraph/Makefile b/langgraph/Makefile new file mode 100644 index 000000000..4bfd87862 --- /dev/null +++ b/langgraph/Makefile @@ -0,0 +1,67 @@ +.PHONY: all format lint test tests test_watch integration_tests docker_tests help extended_tests + +# Default target executed when no arguments are given to make. +all: help + +# Define a variable for the test file path. +TEST_FILE ?= tests/unit_tests/ + +test: + python -m pytest $(TEST_FILE) + +integration_tests: + python -m pytest tests/integration_tests + +test_watch: + python -m ptw --snapshot-update --now . -- -vv tests/unit_tests + +test_profile: + python -m pytest -vv tests/unit_tests/ --profile-svg + +extended_tests: + python -m pytest --only-extended $(TEST_FILE) + + +###################### +# LINTING AND FORMATTING +###################### + +# Define a variable for Python and notebook files. +PYTHON_FILES=src/ +MYPY_CACHE=.mypy_cache +lint format: PYTHON_FILES=. +lint_diff format_diff: PYTHON_FILES=$(shell git diff --name-only --diff-filter=d main | grep -E '\.py$$|\.ipynb$$') +lint_package: PYTHON_FILES=src +lint_tests: PYTHON_FILES=tests +lint_tests: MYPY_CACHE=.mypy_cache_test + +lint lint_diff lint_package lint_tests: + python -m ruff check . + [ "$(PYTHON_FILES)" = "" ] || python -m ruff format $(PYTHON_FILES) --diff + [ "$(PYTHON_FILES)" = "" ] || python -m ruff check --select I $(PYTHON_FILES) + [ "$(PYTHON_FILES)" = "" ] || python -m mypy --strict $(PYTHON_FILES) + [ "$(PYTHON_FILES)" = "" ] || mkdir -p $(MYPY_CACHE) && python -m mypy --strict $(PYTHON_FILES) --cache-dir $(MYPY_CACHE) + +format format_diff: + ruff format $(PYTHON_FILES) + ruff check --select I --fix $(PYTHON_FILES) + +spell_check: + codespell --toml pyproject.toml + +spell_fix: + codespell --toml pyproject.toml -w + +###################### +# HELP +###################### + +help: + @echo '----' + @echo 'format - run code formatters' + @echo 'lint - run linters' + @echo 'test - run unit tests' + @echo 'tests - run unit tests' + @echo 'test TEST_FILE= - run all tests in file' + @echo 'test_watch - run unit tests in watch mode' + diff --git a/langgraph/README.md b/langgraph/README.md new file mode 100644 index 000000000..18ebe0a9d --- /dev/null +++ b/langgraph/README.md @@ -0,0 +1,80 @@ +# New LangGraph Project + +[![CI](https://github.com/langchain-ai/new-langgraph-project/actions/workflows/unit-tests.yml/badge.svg)](https://github.com/langchain-ai/new-langgraph-project/actions/workflows/unit-tests.yml) +[![Integration Tests](https://github.com/langchain-ai/new-langgraph-project/actions/workflows/integration-tests.yml/badge.svg)](https://github.com/langchain-ai/new-langgraph-project/actions/workflows/integration-tests.yml) + +This template demonstrates a simple application implemented using [LangGraph](https://github.com/langchain-ai/langgraph), designed for showing how to get started with [LangGraph Server](https://langchain-ai.github.io/langgraph/concepts/langgraph_server/#langgraph-server) and using [LangGraph Studio](https://langchain-ai.github.io/langgraph/concepts/langgraph_studio/), a visual debugging IDE. + +
+ Graph view in LangGraph studio UI +
+ +The core logic defined in `src/agent/graph.py`, showcases an single-step application that responds with a fixed string and the configuration provided. + +You can extend this graph to orchestrate more complex agentic workflows that can be visualized and debugged in LangGraph Studio. + +## Getting Started + + + + + +1. Install dependencies, along with the [LangGraph CLI](https://langchain-ai.github.io/langgraph/concepts/langgraph_cli/), which will be used to run the server. + +```bash +cd path/to/your/app +pip install -e . "langgraph-cli[inmem]" +``` + +2. (Optional) Customize the code and project as needed. Create a `.env` file if you need to use secrets. + +```bash +cp .env.example .env +``` + +If you want to enable LangSmith tracing, add your LangSmith API key to the `.env` file. + +```text +# .env +LANGSMITH_API_KEY=lsv2... +``` + +3. Start the LangGraph Server. + +```shell +langgraph dev +``` + +For more information on getting started with LangGraph Server, [see here](https://langchain-ai.github.io/langgraph/tutorials/langgraph-platform/local-server/). + +## How to customize + +1. **Define configurable parameters**: Modify the `Configuration` class in the `graph.py` file to expose the arguments you want to configure. For example, in a chatbot application you may want to define a dynamic system prompt or LLM to use. For more information on configurations in LangGraph, [see here](https://langchain-ai.github.io/langgraph/concepts/low_level/?h=configuration#configuration). + +2. **Extend the graph**: The core logic of the application is defined in [graph.py](./src/agent/graph.py). You can modify this file to add new nodes, edges, or change the flow of information. + +## Development + +While iterating on your graph in LangGraph Studio, you can edit past state and rerun your app from previous states to debug specific nodes. Local changes will be automatically applied via hot reload. + +Follow-up requests extend the same thread. You can create an entirely new thread, clearing previous history, using the `+` button in the top right. + +For more advanced features and examples, refer to the [LangGraph documentation](https://langchain-ai.github.io/langgraph/). These resources can help you adapt this template for your specific use case and build more sophisticated conversational agents. + +LangGraph Studio also integrates with [LangSmith](https://smith.langchain.com/) for more in-depth tracing and collaboration with teammates, allowing you to analyze and optimize your chatbot's performance. + + diff --git a/langgraph/langgraph.json b/langgraph/langgraph.json new file mode 100644 index 000000000..9c4966ecc --- /dev/null +++ b/langgraph/langgraph.json @@ -0,0 +1,8 @@ +{ + "dependencies": ["."], + "graphs": { + "agent": "./src/agent/graph.py:graph" + }, + "env": ".env", + "image_distro": "wolfi" +} diff --git a/langgraph/openaitest.py b/langgraph/openaitest.py new file mode 100644 index 000000000..2e0aee6d9 --- /dev/null +++ b/langgraph/openaitest.py @@ -0,0 +1,13 @@ +import openai +import os +from dotenv import load_dotenv + +load_dotenv() +openai.api_key = os.getenv("OPENAI_API_KEY") + +# New OpenAI v1.x syntax +client = openai.OpenAI(api_key=openai.api_key) +models = client.models.list() + +for model in models.data: + print(model.id) \ No newline at end of file diff --git a/langgraph/pyproject.toml b/langgraph/pyproject.toml new file mode 100644 index 000000000..a6237d4f9 --- /dev/null +++ b/langgraph/pyproject.toml @@ -0,0 +1,65 @@ +[project] +name = "agent" +version = "0.0.1" +description = "Starter template for making a new agent LangGraph." +authors = [ + { name = "William Fu-Hinthorn", email = "13333726+hinthornw@users.noreply.github.com" }, +] +readme = "README.md" +license = { text = "MIT" } +requires-python = ">=3.9" +dependencies = [ + "langgraph>=0.2.6", + "python-dotenv>=1.0.1", +] + + +[project.optional-dependencies] +dev = ["mypy>=1.11.1", "ruff>=0.6.1"] + +[build-system] +requires = ["setuptools>=73.0.0", "wheel"] +build-backend = "setuptools.build_meta" + +[tool.setuptools] +packages = ["langgraph.templates.agent", "agent"] +[tool.setuptools.package-dir] +"langgraph.templates.agent" = "src/agent" +"agent" = "src/agent" + + +[tool.setuptools.package-data] +"*" = ["py.typed"] + +[tool.ruff] +lint.select = [ + "E", # pycodestyle + "F", # pyflakes + "I", # isort + "D", # pydocstyle + "D401", # First line should be in imperative mood + "T201", + "UP", +] +lint.ignore = [ + "UP006", + "UP007", + # We actually do want to import from typing_extensions + "UP035", + # Relax the convention by _not_ requiring documentation for every function parameter. + "D417", + "E501", +] +[tool.ruff.lint.per-file-ignores] +"tests/*" = ["D", "UP"] +[tool.ruff.lint.pydocstyle] +convention = "google" + +[dependency-groups] +dev = [ + "anyio>=4.7.0", + "langgraph-cli[inmem]>=0.2.8", + "mypy>=1.13.0", + "pytest>=8.3.5", + "ruff>=0.8.2", +] diff --git a/langgraph/src/agent/__init__.py b/langgraph/src/agent/__init__.py new file mode 100644 index 000000000..743c2a0fe --- /dev/null +++ b/langgraph/src/agent/__init__.py @@ -0,0 +1,8 @@ +"""New LangGraph Agent. + +This module defines a custom graph. +""" + +from agent.graph import graph + +__all__ = ["graph"] diff --git a/langgraph/src/agent/graph.py b/langgraph/src/agent/graph.py new file mode 100644 index 000000000..4a17ac567 --- /dev/null +++ b/langgraph/src/agent/graph.py @@ -0,0 +1,158 @@ +"""LangGraph single-node graph template, with OpenAI API integration and .env support.""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Dict, Optional, TypedDict + +from langchain_core.runnables import RunnableConfig +from langgraph.graph import StateGraph +import openai + +import os +from dotenv import load_dotenv + +from .raw_data_analyzer import RawDataAnalyzer +from .schema_parser import parse_schema_content + +# Load environment variables from .env. +load_dotenv() +OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") +client = openai.AsyncOpenAI(api_key=OPENAI_API_KEY) + +class Configuration(TypedDict): + """Configurable parameters for the agent.""" + my_configurable_param: str + openai_model: str + +@dataclass +class State: + """Input state for the agent.""" + file_path: str = "" + raw_data_result: Optional[Dict[str, Any]] = None + schema_content: str = "" + schema_result: Optional[Dict[str, Any]] = None + changeme: str = "example" + +async def call_model(state: State, config: RunnableConfig) -> Dict[str, Any]: + """Process input and returns output using OpenAI API.""" + configuration = config["configurable"] + model = configuration.get("openai_model", "gpt-3.5-turbo") + + # Build context from schema parser results + schema_context = "" + if state.schema_result and "error" not in state.schema_result: + schema_context = f""" +Schema Analysis Results: +- Total columns: {state.schema_result.get('total_columns', 0)} +- Required columns: {state.schema_result.get('required_columns', 0)} +- Optional columns: {state.schema_result.get('optional_columns', 0)} +- Schema type: {state.schema_result.get('schema_type', 'unknown')} + +Column Details: +""" + for col in state.schema_result.get('columns', []): + schema_context += f"- {col['name']}: {col['data_type']} (required: {col['required']}, nullable: {col['nullable']})\n" + if col.get('description'): + schema_context += f" Description: {col['description']}\n" + + # Build context from raw data analysis + raw_data_context = "" + if state.raw_data_result and "error" not in state.raw_data_result: + raw_data_context = f""" +Raw Data Analysis Results: +- File: {state.raw_data_result.get('file_path', 'unknown')} +- Total rows: {state.raw_data_result.get('total_rows', 0)} +- Total columns: {state.raw_data_result.get('total_columns', 0)} +""" + + prompt = f"""You are an AutoEDA (Automated Exploratory Data Analysis) assistant. +Based on the data analysis and schema information below, provide insights and recommendations for exploratory data analysis. + +{raw_data_context} + +{schema_context} + +Configured parameter: {configuration.get('my_configurable_param')} +Additional context: {state.changeme} + +Please provide: +1. Key insights about the dataset structure +2. Recommended exploratory data analysis steps +3. Potential data quality issues to investigate +4. Suggested visualizations or analysis techniques +""" + + response = await client.chat.completions.create(model=model, + messages=[{"role": "user", "content": prompt}], + temperature=0.7, + max_tokens=512) + + output_text = response.choices[0].message.content + + return { + "changeme": output_text + } + +async def analyze_raw_data(state: State, config: RunnableConfig) -> Dict[str, Any]: + """Analyze raw data file and generate schema.""" + if not state.file_path: + return {"raw_data_result": {"error": "No file path provided"}} + + analyzer = RawDataAnalyzer() + result = analyzer.analyze_file(state.file_path) + + if result.error_message: + return {"raw_data_result": {"error": result.error_message}} + + # Convert result to dict for state + raw_data_result = { + "file_path": result.file_path, + "total_rows": result.total_rows, + "total_columns": result.total_columns, + "suggested_schema": result.suggested_schema, + "analysis_metadata": result.analysis_metadata + } + + return {"raw_data_result": raw_data_result} + +async def parse_schema(state: State, config: RunnableConfig) -> Dict[str, Any]: + """Parse schema content and extract column information.""" + if not state.schema_content: + return {"schema_result": {"error": "No schema content provided"}} + + result = parse_schema_content(state.schema_content) + + if result.error_message: + return {"schema_result": {"error": result.error_message}} + + # Convert result to dict for state + schema_result = { + "total_columns": result.total_columns, + "required_columns": result.required_columns, + "optional_columns": result.optional_columns, + "schema_type": result.schema_type, + "columns": [ + { + "name": col.name, + "data_type": col.data_type, + "required": col.required, + "description": col.description, + "nullable": col.nullable + } for col in result.columns + ] + } + + return {"schema_result": schema_result} + +# Define the graph +graph = ( + StateGraph(State, config_schema=Configuration) + .add_node("analyze_raw_data", analyze_raw_data) + .add_node("parse_schema", parse_schema) + .add_node("call_model", call_model) + .add_edge("__start__", "analyze_raw_data") + .add_edge("analyze_raw_data", "parse_schema") + .add_edge("parse_schema", "call_model") + .compile(name="AutoEDA Agent Graph") +) \ No newline at end of file diff --git a/langgraph/src/agent/raw_data_analyzer.py b/langgraph/src/agent/raw_data_analyzer.py new file mode 100644 index 000000000..311895b95 --- /dev/null +++ b/langgraph/src/agent/raw_data_analyzer.py @@ -0,0 +1,620 @@ +#!/usr/bin/env python + +""" +Raw Data Analyzer for AutoEDA Agent. + +Analyzes raw data files (CSV, JSON, Parquet, Excel) and generates schema.json files +for use with the schema parser module. + +Import as: + +import raw_data_analyzer as rdanal +""" + +import argparse +import json +import logging +from dataclasses import dataclass +from pathlib import Path +from typing import Any, Dict, List, Optional, Union + +import numpy as np +import pandas as pd +import warnings + +import helpers.hdbg as hdbg +import helpers.hio as hio +import helpers.hpandas as hpandas + +# Configure logging. +logging.basicConfig(level=logging.INFO) +_LOG = logging.getLogger(__name__) + +@dataclass +class ColumnAnalysis: + """ + Represent analysis results for a single column. + """ + name: str + data_type: str + nullable: bool + null_count: int + unique_count: int + sample_values: List[Any] + min_value: Optional[Any] = None + max_value: Optional[Any] = None + mean_value: Optional[float] = None + std_value: Optional[float] = None + pattern: Optional[str] = None + format_hint: Optional[str] = None + + +@dataclass +class DataAnalysisResult: + """ + Represent the complete data analysis result. + """ + file_path: str + total_rows: int + total_columns: int + columns: List[ColumnAnalysis] + suggested_schema: Dict[str, Any] + analysis_metadata: Dict[str, Any] + error_message: Optional[str] = None + + +class RawDataAnalyzer: + """ + Analyze raw data files and generate schema definitions. + + Supported formats: + - CSV files + - JSON files + - Parquet files + - Excel files + """ + + def __init__( + self, + *, + sample_size: int = 10000, + max_unique_values: int = 100, + datetime_formats: Optional[List[str]] = None, + ) -> None: + """ + Initialize the raw data analyzer. + + :param sample_size: maximum number of rows to sample for analysis + :param max_unique_values: maximum unique values to consider for enum types + :param datetime_formats: list of datetime formats to try for detection + """ + self.sample_size = sample_size + self.max_unique_values = max_unique_values + self.datetime_formats = datetime_formats or [ + "%Y-%m-%d", + "%Y-%m-%d %H:%M:%S", + "%Y-%m-%dT%H:%M:%S", + "%Y-%m-%dT%H:%M:%SZ", + "%m/%d/%Y", + "%d/%m/%Y", + "%Y%m%d", + ] + self.supported_formats = [".csv", ".json", ".parquet", ".xlsx", ".xls"] + + def analyze_file(self, file_path: str) -> DataAnalysisResult: + """ + Analyze a data file and generate schema information. + + :param file_path: path to the data file to analyze + :return: analysis results with suggested schema + """ + try: + path = Path(file_path) + + if not path.exists(): + error_msg = f"Data file not found: {file_path}" + return self._create_error_result(file_path, error_msg) + + if not self._is_supported_format(path.suffix): + error_msg = f"Unsupported file format: {path.suffix}" + return self._create_error_result(file_path, error_msg) + + # Load data based on file format. + df = self._load_data(file_path) + + # Sample data if it's too large. + if len(df) > self.sample_size: + _LOG.info( + "Sampling %d rows from %d total rows", + self.sample_size, + len(df), + ) + df = df.sample(n=self.sample_size, random_state=42) + + # Analyze each column. + columns_analysis = self._analyze_columns(df) + + # Generate suggested schema. + suggested_schema = self._generate_schema(columns_analysis, df) + + # Create analysis metadata. + analysis_metadata = self._create_analysis_metadata(file_path, df) + + _LOG.info( + "Successfully analyzed %d columns from %s", + len(columns_analysis), + file_path, + ) + + return DataAnalysisResult( + file_path=file_path, + total_rows=len(df), + total_columns=len(df.columns), + columns=columns_analysis, + suggested_schema=suggested_schema, + analysis_metadata=analysis_metadata, + ) + + except Exception as e: + _LOG.error("Error analyzing file %s: %s", file_path, e) + error_msg = f"Failed to analyze file: {str(e)}" + return self._create_error_result(file_path, error_msg) + + def save_schema( + self, + analysis_result: DataAnalysisResult, + output_path: str, + *, + include_analysis_metadata: bool = True, + ) -> None: + """ + Save the generated schema to a JSON file. + + :param analysis_result: analysis result containing the schema + :param output_path: path where to save the schema.json file + :param include_analysis_metadata: whether to include analysis metadata + """ + try: + schema_data = analysis_result.suggested_schema.copy() + + if include_analysis_metadata: + schema_data["_analysis_metadata"] = analysis_result.analysis_metadata + + # Ensure output directory exists. + output_dir = Path(output_path).parent + hio.create_dir(str(output_dir), incremental=True) + + # Save schema to file. + with open(output_path, "w", encoding="utf-8") as f: + json.dump(schema_data, f, indent=2, default=str) + + _LOG.info("Schema saved to %s", output_path) + + except Exception as e: + _LOG.error("Error saving schema to %s: %s", output_path, e) + raise + + def _load_data(self, file_path: str) -> pd.DataFrame: + """ + Load data from file based on format. + + :param file_path: path to the data file + :return: loaded DataFrame + """ + path = Path(file_path) + file_extension = path.suffix.lower() + + if file_extension == ".csv": + # Try different encodings and separators. + for encoding in ["utf-8", "latin-1", "cp1252"]: + for sep in [",", ";", "\t"]: + try: + df = pd.read_csv( + file_path, + encoding=encoding, + sep=sep, + nrows=self.sample_size, + ) + if len(df.columns) > 1: # Good indicator of correct separator. + _LOG.debug( + "Successfully loaded CSV with encoding=%s, sep='%s'", + encoding, + sep, + ) + return df + except Exception: + continue + + # Fallback to default pandas behavior. + return pd.read_csv(file_path, nrows=self.sample_size) + + elif file_extension == ".json": + return pd.read_json(file_path, lines=True, nrows=self.sample_size) + + elif file_extension == ".parquet": + return pd.read_parquet(file_path) + + elif file_extension in [".xlsx", ".xls"]: + return pd.read_excel(file_path, nrows=self.sample_size) + + else: + raise ValueError(f"Unsupported file format: {file_extension}") + + def _analyze_columns(self, df: pd.DataFrame) -> List[ColumnAnalysis]: + """ + Analyze each column in the DataFrame. + + :param df: DataFrame to analyze + :return: list of column analysis results + """ + columns_analysis = [] + + for col_name in df.columns: + col_data = df[col_name] + analysis = self._analyze_single_column(col_name, col_data) + columns_analysis.append(analysis) + + return columns_analysis + + def _analyze_single_column( + self, + col_name: str, + col_data: pd.Series, + ) -> ColumnAnalysis: + """ + Analyze a single column. + + :param col_name: name of the column + :param col_data: column data as pandas Series + :return: column analysis result + """ + # Basic statistics. + null_count = col_data.isnull().sum() + unique_count = col_data.nunique() + nullable = null_count > 0 + + # Sample non-null values. + non_null_data = col_data.dropna() + sample_values = ( + non_null_data.head(5).tolist() if len(non_null_data) > 0 else [] + ) + + # Infer data type. + data_type, format_hint = self._infer_data_type(non_null_data) + + # Calculate statistics for numeric columns. + min_value = None + max_value = None + mean_value = None + std_value = None + + if data_type in ["integer", "float"] and len(non_null_data) > 0: + try: + numeric_data = pd.to_numeric(non_null_data, errors="coerce") + min_value = float(numeric_data.min()) + max_value = float(numeric_data.max()) + mean_value = float(numeric_data.mean()) + std_value = float(numeric_data.std()) + except Exception: + pass + + # Detect patterns for string columns. + pattern = None + if data_type == "string" and len(non_null_data) > 0: + pattern = self._detect_pattern(non_null_data) + + return ColumnAnalysis( + name=col_name, + data_type=data_type, + nullable=nullable, + null_count=int(null_count), + unique_count=int(unique_count), + sample_values=sample_values, + min_value=min_value, + max_value=max_value, + mean_value=mean_value, + std_value=std_value, + pattern=pattern, + format_hint=format_hint, + ) + + def _infer_data_type(self, data: pd.Series) -> tuple[str, Optional[str]]: + """ + Infer the data type of a column. + + :param data: column data (non-null values) + :return: tuple of (data_type, format_hint) + """ + if len(data) == 0: + return "string", None + + # Check for boolean. + if data.dtype == bool or set(data.astype(str).str.lower()) <= { + "true", + "false", + "t", + "f", + "yes", + "no", + "y", + "n", + "1", + "0", + }: + return "boolean", None + + # Check for integer. + if data.dtype in ["int64", "int32", "int16", "int8"]: + return "integer", None + + # Check for float. + if data.dtype in ["float64", "float32"]: + return "float", None + + # Try to convert to numeric. + try: + numeric_data = pd.to_numeric(data, errors="coerce") + if not numeric_data.isnull().all(): + # Check if all values are integers. + if numeric_data.fillna(0).apply(lambda x: x.is_integer()).all(): + return "integer", None + else: + return "float", None + except Exception: + pass + + # Check for datetime. + datetime_type, format_hint = self._check_datetime(data) + if datetime_type: + return datetime_type, format_hint + + # Default to string. + return "string", None + + def _check_datetime(self, data: pd.Series) -> tuple[Optional[str], Optional[str]]: + """ + Check if data represents datetime values with improved detection. + + :param data: column data to check + :return: tuple of (datetime_type, format_hint) + """ + # Skip if data looks purely numeric. + try: + pd.to_numeric(data.head(10), errors="raise") + return None, None + except (ValueError, TypeError): + pass + + # Sample a subset for testing. + sample_data = data.head(min(100, len(data))) + + # Try specific datetime formats first. + for fmt in self.datetime_formats: + try: + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + parsed = pd.to_datetime(sample_data, format=fmt, errors="coerce") + + # Check success rate. + success_rate = (1 - parsed.isnull().sum() / len(sample_data)) + if success_rate >= self.datetime_threshold: + # Determine if it's date or datetime. + non_null_parsed = parsed.dropna() + if len(non_null_parsed) > 0: + # Check if all times are midnight (date-only). + if all(t.time() == non_null_parsed.iloc[0].time() for t in non_null_parsed.head(10)): + return "date", fmt + else: + return "datetime", fmt + except Exception: + continue + + # Try pandas automatic parsing as last resort. + try: + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + parsed = pd.to_datetime(sample_data, errors="coerce", infer_datetime_format=True) + + success_rate = (1 - parsed.isnull().sum() / len(sample_data)) + if success_rate >= self.datetime_threshold: + return "datetime", "auto" + except Exception: + pass + + return None, None + + def _detect_pattern(self, data: pd.Series) -> Optional[str]: + """ + Detect common patterns in string data. + + :param data: string column data + :return: detected pattern or None + """ + str_data = data.astype(str) + + # Check for common patterns. + patterns = { + "email": r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$", + "phone": r"^[\+]?[1-9]?[0-9]{7,15}$", + "url": r"^https?://[^\s/$.?#].[^\s]*$", + "uuid": r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", + "ip_address": r"^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$", + } + + for pattern_name, pattern_regex in patterns.items(): + if str_data.str.match(pattern_regex, case=False).mean() > 0.8: + return pattern_name + + return None + + def _generate_schema( + self, + columns_analysis: List[ColumnAnalysis], + df: pd.DataFrame, + ) -> Dict[str, Any]: + """ + Generate JSON Schema from column analysis. + + :param columns_analysis: list of analyzed columns + :param df: original DataFrame + :return: JSON Schema dictionary + """ + properties = {} + required_fields = [] + + for col in columns_analysis: + col_schema = { + "type": self._map_to_json_schema_type(col.data_type), + "description": f"Column '{col.name}' with {col.unique_count} unique values", + } + + # Add format hint if available. + if col.format_hint and col.format_hint != "auto": + col_schema["format"] = col.format_hint + + # Add constraints for numeric fields. + if col.data_type in ["integer", "float"]: + if col.min_value is not None: + col_schema["minimum"] = col.min_value + if col.max_value is not None: + col_schema["maximum"] = col.max_value + + # Add enum for low-cardinality categorical fields. + if ( + col.data_type == "string" + and col.unique_count <= self.max_unique_values + and col.unique_count > 1 + ): + unique_values = df[col.name].dropna().unique().tolist() + col_schema["enum"] = unique_values[:50] # Limit enum size. + + # Add pattern if detected. + if col.pattern: + col_schema["pattern"] = col.pattern + + properties[col.name] = col_schema + + # Mark as required if no null values. + if not col.nullable: + required_fields.append(col.name) + + schema = { + "type": "object", + "title": "Generated Schema", + "description": f"Auto-generated schema for data with {len(columns_analysis)} columns", + "properties": properties, + } + + if required_fields: + schema["required"] = required_fields + + return schema + + def _map_to_json_schema_type(self, data_type: str) -> str: + """ + Map internal data types to JSON Schema types. + + :param data_type: internal data type + :return: JSON Schema type + """ + type_mapping = { + "integer": "integer", + "float": "number", + "boolean": "boolean", + "string": "string", + "datetime": "string", + "date": "string", + "object": "object", + "array": "array", + } + return type_mapping.get(data_type, "string") + + def _create_analysis_metadata( + self, + file_path: str, + df: pd.DataFrame, + ) -> Dict[str, Any]: + """ + Create metadata about the analysis process. + + :param file_path: path to analyzed file + :param df: analyzed DataFrame + :return: analysis metadata + """ + return { + "analysis_timestamp": pd.Timestamp.now().isoformat(), + "analyzer_version": "1.0.0", + "source_file": file_path, + "sample_size": min(len(df), self.sample_size), + "total_rows_analyzed": len(df), + "total_columns_analyzed": len(df.columns), + "file_size_bytes": Path(file_path).stat().st_size, + "analysis_settings": { + "max_unique_values": self.max_unique_values, + "datetime_formats": self.datetime_formats, + }, + } + + def _is_supported_format(self, file_extension: str) -> bool: + """ + Check if file format is supported. + + :param file_extension: file extension to check + :return: whether the format is supported + """ + return file_extension.lower() in self.supported_formats + + def _create_error_result( + self, + file_path: str, + error_message: str, + ) -> DataAnalysisResult: + """ + Create a DataAnalysisResult with error information. + + :param file_path: path to the file that caused the error + :param error_message: error message to include + :return: DataAnalysisResult object with error details + """ + return DataAnalysisResult( + file_path=file_path, + total_rows=0, + total_columns=0, + columns=[], + suggested_schema={}, + analysis_metadata={}, + error_message=error_message, + ) + + +def main(): + parser = argparse.ArgumentParser(description="Analyze raw data files and generate schema information.") + parser.add_argument("file", type=str, help="Path to the data file to analyze") + args = parser.parse_args() + + analyzer = RawDataAnalyzer() + result = analyzer.analyze_file(args.file) + if result.error_message: + _LOG.error("Error: %s", result.error_message) + else: + _LOG.info("File: %s", result.file_path) + _LOG.info("Total rows: %d", result.total_rows) + _LOG.info("Total columns: %d", result.total_columns) + _LOG.info("Columns:") + for col in result.columns: + _LOG.info(" - %s: %s, nullable=%s", col.name, col.data_type, col.nullable) + _LOG.info("Suggested schema: %s", result.suggested_schema) + _LOG.info("Metadata: %s", result.analysis_metadata) + + # Save suggested schema to JSON file + schema_path = args.file + ".schema.json" + try: + with open(schema_path, "w") as f: + json.dump(result.suggested_schema, f, indent=2) + _LOG.info("Saved schema to %s", schema_path) + except Exception as e: + _LOG.error("Failed to save schema to %s: %s", schema_path, e) + +if __name__ == "__main__": + main() + diff --git a/langgraph/src/agent/schema_parser.py b/langgraph/src/agent/schema_parser.py new file mode 100755 index 000000000..9f3d08d09 --- /dev/null +++ b/langgraph/src/agent/schema_parser.py @@ -0,0 +1,677 @@ +#!/usr/bin/env python + +""" +Schema Parser Module for AutoEDA Agent. + +A standalone module to parse JSON/YAML schema files and extract column information +for automated exploratory data analysis. + +Import as: + +import schema_parser as schpar +""" + +import argparse +import json +import logging +import os +from dataclasses import dataclass +from pathlib import Path +from typing import Any, Dict, List, Optional + +import yaml + +import helpers.hdbg as hdbg +import helpers.hio as hio + +# Configure logging. +logging.basicConfig(level=logging.INFO) +_LOG = logging.getLogger(__name__) + + +@dataclass +class ColumnInfo: + """ + Represent information about a single column. + """ + name: str + data_type: str + required: bool = False + description: str = "" + nullable: bool = True + default: Any = None + constraints: Optional[Dict[str, Any]] = None + metadata: Optional[Dict[str, Any]] = None + + def __post_init__(self) -> None: + """ + Initialize default values for optional fields. + """ + if self.constraints is None: + self.constraints = {} + if self.metadata is None: + self.metadata = {} + + +@dataclass +class ParsedSchema: + """ + Represent the complete parsed schema result. + """ + columns: List[ColumnInfo] + raw_schema: Dict[str, Any] + schema_type: str + total_columns: int + required_columns: int + optional_columns: int + error_message: Optional[str] = None + + def __post_init__(self) -> None: + """ + Calculate derived fields from columns. + """ + self.total_columns = len(self.columns) + self.required_columns = sum(1 for col in self.columns if col.required) + self.optional_columns = self.total_columns - self.required_columns + + +class SchemaParser: + """ + Parse various schema formats and extract column information. + + Supported formats: + - JSON Schema + - Custom column definitions + - YAML schema files + - Generic key-value structures + """ + + def __init__( + self, + *, + include_metadata: bool = True, + output_format: str = "detailed", + ) -> None: + """ + Initialize the schema parser. + + :param include_metadata: whether to include metadata in parsed results + :param output_format: output format, either "detailed" or "simple" + """ + hdbg.dassert_in(output_format, ["detailed", "simple"]) + self.include_metadata = include_metadata + self.output_format = output_format + self.supported_formats = ["json", "yaml", "yml"] + + def parse_file(self, file_path: str) -> ParsedSchema: + """ + Parse schema from a file. + + :param file_path: path to the schema file + :return: parsed schema object with results + """ + try: + path = Path(file_path) + + if not path.exists(): + error_msg = f"Schema file not found: {file_path}" + return self._create_error_result(error_msg) + + if not self._is_supported_format(path.suffix): + error_msg = f"Unsupported file format: {path.suffix}" + return self._create_error_result(error_msg) + + # Read file content. + content = hio.from_file(str(path)) + + return self.parse_content(content, str(path)) + + except Exception as e: + _LOG.error("Error parsing file %s: %s", file_path, e) + error_msg = f"Failed to parse file: {str(e)}" + return self._create_error_result(error_msg) + + def parse_content( + self, + content: str, + *, + source: str = "content", + ) -> ParsedSchema: + """ + Parse schema from string content. + + :param content: schema content as string + :param source: source identifier for logging + :return: parsed schema object with results + """ + try: + schema_data = self._parse_schema_content(content) + schema_type = self._detect_schema_type(schema_data) + columns = self._extract_columns(schema_data, schema_type) + + _LOG.info( + "Successfully parsed %d columns from %s", + len(columns), + source, + ) + + return ParsedSchema( + columns=columns, + raw_schema=schema_data, + schema_type=schema_type, + total_columns=len(columns), + required_columns=0, # Will be calculated in __post_init__. + optional_columns=0, # Will be calculated in __post_init__. + ) + + except Exception as e: + _LOG.error("Error parsing content from %s: %s", source, e) + error_msg = f"Failed to parse content: {str(e)}" + return self._create_error_result(error_msg) + + def to_dict(self, parsed_schema: ParsedSchema) -> Dict[str, Any]: + """ + Convert ParsedSchema to dictionary format. + + :param parsed_schema: parsed schema object to convert + :return: dictionary representation of the schema + """ + return { + "columns": [ + { + "name": col.name, + "data_type": col.data_type, + "required": col.required, + "description": col.description, + "nullable": col.nullable, + "default": col.default, + "constraints": col.constraints, + "metadata": col.metadata if self.include_metadata else {}, + } + for col in parsed_schema.columns + ], + "schema_info": { + "schema_type": parsed_schema.schema_type, + "total_columns": parsed_schema.total_columns, + "required_columns": parsed_schema.required_columns, + "optional_columns": parsed_schema.optional_columns, + }, + "raw_schema": parsed_schema.raw_schema, + "error_message": parsed_schema.error_message, + } + + def to_dataframe_schema(self, parsed_schema: ParsedSchema) -> Dict[str, str]: + """ + Convert to simple column_name: data_type mapping for pandas. + + :param parsed_schema: parsed schema object to convert + :return: simple mapping of column names to data types + """ + return {col.name: col.data_type for col in parsed_schema.columns} + + def _parse_schema_content(self, content: str) -> Dict[str, Any]: + """ + Parse content as JSON or YAML. + + :param content: raw content string to parse + :return: parsed data as dictionary + """ + content = content.strip() + + # Try JSON first. + try: + return json.loads(content) + except json.JSONDecodeError: + pass + + # Try YAML. + try: + return yaml.safe_load(content) + except yaml.YAMLError as e: + raise ValueError(f"Invalid JSON/YAML format: {e}") + + def _detect_schema_type(self, schema_data: Dict[str, Any]) -> str: + """ + Detect the type of schema format. + + :param schema_data: parsed schema data + :return: detected schema type + """ + if "properties" in schema_data and "type" in schema_data: + return "json_schema" + elif "columns" in schema_data: + return "custom_columns" + elif "fields" in schema_data: + return "fields_format" + elif any( + isinstance(v, dict) and ("type" in v or "dataType" in v) + for v in schema_data.values() + ): + return "generic_properties" + else: + return "unknown" + + def _extract_columns( + self, + schema_data: Dict[str, Any], + schema_type: str, + ) -> List[ColumnInfo]: + """ + Extract column information based on schema type. + + :param schema_data: parsed schema data + :param schema_type: detected schema type + :return: list of column information objects + """ + extractors = { + "json_schema": self._extract_from_json_schema, + "custom_columns": self._extract_from_custom_format, + "fields_format": self._extract_from_fields_format, + "generic_properties": self._extract_from_generic_format, + "unknown": self._extract_from_unknown_format, + } + + extractor = extractors.get(schema_type, self._extract_from_unknown_format) + return extractor(schema_data) + + def _extract_from_json_schema( + self, + schema_data: Dict[str, Any], + ) -> List[ColumnInfo]: + """ + Extract columns from JSON Schema format. + + :param schema_data: JSON schema data + :return: list of column information objects + """ + columns = [] + properties = schema_data.get("properties", {}) + required_fields = set(schema_data.get("required", [])) + + for column_name, column_info in properties.items(): + constraints = {} + metadata = {} + + # Extract constraints. + for key in [ + "minimum", + "maximum", + "minLength", + "maxLength", + "pattern", + "enum", + ]: + if key in column_info: + constraints[key] = column_info[key] + + # Extract metadata if enabled. + if self.include_metadata: + excluded_keys = [ + "type", + "description", + "minimum", + "maximum", + "minLength", + "maxLength", + "pattern", + "enum", + ] + metadata = { + k: v + for k, v in column_info.items() + if k not in excluded_keys + } + + column = ColumnInfo( + name=column_name, + data_type=self._map_json_schema_type( + column_info.get("type", "string") + ), + required=column_name in required_fields, + description=column_info.get("description", ""), + nullable=not (column_name in required_fields), + default=column_info.get("default"), + constraints=constraints, + metadata=metadata, + ) + + columns.append(column) + + return columns + + def _extract_from_custom_format( + self, + schema_data: Dict[str, Any], + ) -> List[ColumnInfo]: + """ + Extract columns from custom columns format. + + :param schema_data: custom format schema data + :return: list of column information objects + """ + columns = [] + columns_data = schema_data.get("columns", []) + + for column_info in columns_data: + if not isinstance(column_info, dict): + continue + + constraints = column_info.get("constraints", {}) + metadata = {} + + if self.include_metadata: + excluded_keys = [ + "name", + "type", + "required", + "description", + "nullable", + "default", + "constraints", + ] + metadata = { + k: v + for k, v in column_info.items() + if k not in excluded_keys + } + + column = ColumnInfo( + name=column_info.get("name", ""), + data_type=self._normalize_data_type( + column_info.get("type", "string") + ), + required=column_info.get("required", False), + description=column_info.get("description", ""), + nullable=column_info.get("nullable", True), + default=column_info.get("default"), + constraints=constraints, + metadata=metadata, + ) + + columns.append(column) + + return columns + + def _extract_from_fields_format( + self, + schema_data: Dict[str, Any], + ) -> List[ColumnInfo]: + """ + Extract columns from fields format. + + :param schema_data: fields format schema data + :return: list of column information objects + """ + columns = [] + fields_data = schema_data.get("fields", []) + + for field_info in fields_data: + if not isinstance(field_info, dict): + continue + + constraints = field_info.get("constraints", {}) + metadata = {} + + if self.include_metadata: + excluded_keys = [ + "name", + "type", + "optional", + "description", + "constraints", + ] + metadata = { + k: v + for k, v in field_info.items() + if k not in excluded_keys + } + + column = ColumnInfo( + name=field_info.get("name", ""), + data_type=self._normalize_data_type( + field_info.get("type", "string") + ), + required=not field_info.get("optional", True), + description=field_info.get("description", ""), + nullable=field_info.get("optional", True), + constraints=constraints, + metadata=metadata, + ) + + columns.append(column) + + return columns + + def _extract_from_generic_format( + self, + schema_data: Dict[str, Any], + ) -> List[ColumnInfo]: + """ + Extract columns from generic format. + + :param schema_data: generic format schema data + :return: list of column information objects + """ + columns = [] + + for key, value in schema_data.items(): + if not isinstance(value, dict): + continue + + if "type" not in value and "dataType" not in value: + continue + + metadata = {} + if self.include_metadata: + excluded_keys = [ + "type", + "dataType", + "required", + "description", + "nullable", + ] + metadata = { + k: v for k, v in value.items() if k not in excluded_keys + } + + column = ColumnInfo( + name=key, + data_type=self._normalize_data_type( + value.get("type", value.get("dataType", "string")) + ), + required=value.get("required", False), + description=value.get("description", ""), + nullable=value.get("nullable", True), + metadata=metadata, + ) + + columns.append(column) + + return columns + + def _extract_from_unknown_format( + self, + schema_data: Dict[str, Any], + ) -> List[ColumnInfo]: + """ + Extract columns from unknown format by making best guesses. + + :param schema_data: unknown format schema data + :return: list of column information objects + """ + columns = [] + + # Try to extract any key-value pairs as potential columns. + for key, value in schema_data.items(): + if isinstance(value, str): + # Assume string values are data types. + column = ColumnInfo( + name=key, + data_type=self._normalize_data_type(value), + description="Inferred from key-value pair", + ) + columns.append(column) + + return columns + + def _map_json_schema_type(self, json_type: str) -> str: + """ + Map JSON Schema types to standard data types. + + :param json_type: JSON schema type string + :return: normalized data type + """ + type_mapping = { + "string": "string", + "integer": "integer", + "number": "float", + "boolean": "boolean", + "array": "array", + "object": "object", + "null": "null", + } + return type_mapping.get(json_type.lower(), "string") + + def _normalize_data_type(self, data_type: str) -> str: + """ + Normalize various data type representations. + + :param data_type: raw data type string + :return: normalized data type + """ + if not isinstance(data_type, str): + return "string" + + data_type = data_type.lower().strip() + + type_mapping = { + # String types. + "str": "string", + "text": "string", + "varchar": "string", + "char": "string", + "nvarchar": "string", + # Integer types. + "int": "integer", + "int32": "integer", + "int64": "integer", + "bigint": "integer", + "smallint": "integer", + # Float types. + "float": "float", + "float32": "float", + "float64": "float", + "double": "float", + "decimal": "float", + "numeric": "float", + "real": "float", + # Boolean types. + "bool": "boolean", + "bit": "boolean", + # Date/Time types. + "datetime": "datetime", + "datetime64": "datetime", + "timestamp": "datetime", + "date": "date", + "time": "time", + # Other types. + "json": "object", + "jsonb": "object", + "uuid": "string", + "blob": "binary", + "binary": "binary", + } + + return type_mapping.get(data_type, data_type) + + def _is_supported_format(self, file_extension: str) -> bool: + """ + Check if file format is supported. + + :param file_extension: file extension to check + :return: whether the format is supported + """ + return file_extension.lower().lstrip(".") in self.supported_formats + + def _create_error_result(self, error_message: str) -> ParsedSchema: + """ + Create a ParsedSchema with error information. + + :param error_message: error message to include + :return: ParsedSchema object with error details + """ + return ParsedSchema( + columns=[], + raw_schema={}, + schema_type="error", + total_columns=0, + required_columns=0, + optional_columns=0, + error_message=error_message, + ) + + +# ############################################################################# +# Convenience functions. +# ############################################################################# + + +def parse_schema_file( + file_path: str, + *, + include_metadata: bool = True, +) -> ParsedSchema: + """ + Parse a schema file using default settings. + + :param file_path: path to schema file + :param include_metadata: whether to include metadata + :return: parsed schema object + """ + parser = SchemaParser(include_metadata=include_metadata) + return parser.parse_file(file_path) + + +def parse_schema_content( + content: str, + *, + include_metadata: bool = True, +) -> ParsedSchema: + """ + Parse schema content using default settings. + + :param content: schema content as string + :param include_metadata: whether to include metadata + :return: parsed schema object + """ + parser = SchemaParser(include_metadata=include_metadata) + return parser.parse_content(content) + + +def main(): + parser = argparse.ArgumentParser(description="Parse a schema file and log the results.") + parser.add_argument("schema_file", type=str, help="Path to the schema file (JSON)") + args = parser.parse_args() + + # Read schema content from file + try: + with open(args.schema_file, "r") as f: + schema_content = f.read() + except Exception as e: + _LOG.error("Failed to read schema file %s: %s", args.schema_file, e) + return + + result = parse_schema_content(schema_content) + if result.error_message: + _LOG.error("Error: %s", result.error_message) + else: + _LOG.info("Successfully parsed %d columns", result.total_columns) + _LOG.info("Required columns: %d", result.required_columns) + _LOG.info("Optional columns: %d", result.optional_columns) + _LOG.info("Schema type: %s", result.schema_type) + _LOG.info("Columns:") + for col in result.columns: + _LOG.info(" - %s: %s (required: %s)", col.name, col.data_type, col.required) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/langgraph/static/studio_ui.png b/langgraph/static/studio_ui.png new file mode 100644 index 0000000000000000000000000000000000000000..9ccba2b20f1cc9d38aa8f60aa7d934c7d7176846 GIT binary patch literal 287630 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

M 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

^^(|)K%3ajjcxzgh5Xdb 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 None: + inputs = {"changeme": "some_val"} + res = await graph.ainvoke(inputs) + assert res is not None diff --git a/langgraph/tests/unit_tests/__init__.py b/langgraph/tests/unit_tests/__init__.py new file mode 100644 index 000000000..f2900f275 --- /dev/null +++ b/langgraph/tests/unit_tests/__init__.py @@ -0,0 +1 @@ +"""Define any unit tests you may want in this directory.""" diff --git a/langgraph/tests/unit_tests/test_configuration.py b/langgraph/tests/unit_tests/test_configuration.py new file mode 100644 index 000000000..e06f9e38f --- /dev/null +++ b/langgraph/tests/unit_tests/test_configuration.py @@ -0,0 +1,9 @@ +from langgraph.pregel import Pregel + +from agent.graph import graph + + +def test_placeholder() -> None: + # TODO: You can add actual unit tests + # for your graph and other logic here. + assert isinstance(graph, Pregel) From 9d2d768269700fb9703683920f5a6db0e67d3d5b Mon Sep 17 00:00:00 2001 From: maddy Date: Fri, 8 Aug 2025 18:49:33 -0400 Subject: [PATCH 2/4] HelpersTask986: Ran Linter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pre-commit checks: All checks passed ✅ --- langgraph/__init__.py | 0 langgraph/src/agent/graph.py | 136 +++++++---- langgraph/src/agent/raw_data_analyzer.py | 287 ++++++++++++----------- langgraph/src/agent/schema_parser.py | 214 +++++++++-------- 4 files changed, 353 insertions(+), 284 deletions(-) create mode 100644 langgraph/__init__.py diff --git a/langgraph/__init__.py b/langgraph/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/langgraph/src/agent/graph.py b/langgraph/src/agent/graph.py index 4a17ac567..69512c1e9 100644 --- a/langgraph/src/agent/graph.py +++ b/langgraph/src/agent/graph.py @@ -1,41 +1,66 @@ -"""LangGraph single-node graph template, with OpenAI API integration and .env support.""" +""" +LangGraph single-node graph template, with OpenAI API integration and .env +support. + +Import as: -from __future__ import annotations +import langgraph.src.agent.graph as lsraggra +""" -from dataclasses import dataclass +import dataclasses +import os from typing import Any, Dict, Optional, TypedDict -from langchain_core.runnables import RunnableConfig -from langgraph.graph import StateGraph +import dotenv +import langchain_core.runnables as lcru import openai -import os -from dotenv import load_dotenv - -from .raw_data_analyzer import RawDataAnalyzer -from .schema_parser import parse_schema_content +import langgraph.graph as lgg +import langgraph.src.agent.raw_data_analyzer as lsardaan +import langgraph.src.agent.schema_parser as lsagscpa # Load environment variables from .env. -load_dotenv() +dotenv.load_dotenv() OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") client = openai.AsyncOpenAI(api_key=OPENAI_API_KEY) + +# ############################################################################# +# Configuration +# ############################################################################# + + class Configuration(TypedDict): - """Configurable parameters for the agent.""" + """ + Configurable parameters for the agent. + """ + my_configurable_param: str - openai_model: str + openai_model: str + -@dataclass +# ############################################################################# +# State +# ############################################################################# + + +@dataclasses.dataclass class State: - """Input state for the agent.""" + """ + Input state for the agent. + """ + file_path: str = "" raw_data_result: Optional[Dict[str, Any]] = None schema_content: str = "" schema_result: Optional[Dict[str, Any]] = None changeme: str = "example" -async def call_model(state: State, config: RunnableConfig) -> Dict[str, Any]: - """Process input and returns output using OpenAI API.""" + +async def call_model(state: State, config: lcru.RunnableConfig) -> Dict[str, Any]: + """ + Process input and returns output using OpenAI API. + """ configuration = config["configurable"] model = configuration.get("openai_model", "gpt-3.5-turbo") @@ -51,9 +76,12 @@ async def call_model(state: State, config: RunnableConfig) -> Dict[str, Any]: Column Details: """ - for col in state.schema_result.get('columns', []): - schema_context += f"- {col['name']}: {col['data_type']} (required: {col['required']}, nullable: {col['nullable']})\n" - if col.get('description'): + for col in state.schema_result.get("columns", []): + schema_context += ( + f"- {col['name']}: {col['data_type']} " + f"(required: {col['required']}, nullable: {col['nullable']})\n" + ) + if col.get("description"): schema_context += f" Description: {col['description']}\n" # Build context from raw data analysis @@ -66,7 +94,7 @@ async def call_model(state: State, config: RunnableConfig) -> Dict[str, Any]: - Total columns: {state.raw_data_result.get('total_columns', 0)} """ - prompt = f"""You are an AutoEDA (Automated Exploratory Data Analysis) assistant. + prompt = f"""You are an AutoEDA (Automated Exploratory Data Analysis) assistant. Based on the data analysis and schema information below, provide insights and recommendations for exploratory data analysis. {raw_data_context} @@ -83,49 +111,59 @@ async def call_model(state: State, config: RunnableConfig) -> Dict[str, Any]: 4. Suggested visualizations or analysis techniques """ - response = await client.chat.completions.create(model=model, - messages=[{"role": "user", "content": prompt}], - temperature=0.7, - max_tokens=512) + response = await client.chat.completions.create( + model=model, + messages=[{"role": "user", "content": prompt}], + temperature=0.7, + max_tokens=512, + ) output_text = response.choices[0].message.content - return { - "changeme": output_text - } + return {"changeme": output_text} -async def analyze_raw_data(state: State, config: RunnableConfig) -> Dict[str, Any]: - """Analyze raw data file and generate schema.""" + +async def analyze_raw_data( + state: State, _config: lcru.RunnableConfig +) -> Dict[str, Any]: + """ + Analyze raw data file and generate schema. + """ if not state.file_path: return {"raw_data_result": {"error": "No file path provided"}} - - analyzer = RawDataAnalyzer() + + analyzer = lsardaan.RawDataAnalyzer() result = analyzer.analyze_file(state.file_path) - + if result.error_message: return {"raw_data_result": {"error": result.error_message}} - + # Convert result to dict for state raw_data_result = { "file_path": result.file_path, "total_rows": result.total_rows, "total_columns": result.total_columns, "suggested_schema": result.suggested_schema, - "analysis_metadata": result.analysis_metadata + "analysis_metadata": result.analysis_metadata, } - + return {"raw_data_result": raw_data_result} -async def parse_schema(state: State, config: RunnableConfig) -> Dict[str, Any]: - """Parse schema content and extract column information.""" + +async def parse_schema( + state: State, _config: lcru.RunnableConfig +) -> Dict[str, Any]: + """ + Parse schema content and extract column information. + """ if not state.schema_content: return {"schema_result": {"error": "No schema content provided"}} - - result = parse_schema_content(state.schema_content) - + + result = lsagscpa.parse_schema_content(state.schema_content) + if result.error_message: return {"schema_result": {"error": result.error_message}} - + # Convert result to dict for state schema_result = { "total_columns": result.total_columns, @@ -138,16 +176,18 @@ async def parse_schema(state: State, config: RunnableConfig) -> Dict[str, Any]: "data_type": col.data_type, "required": col.required, "description": col.description, - "nullable": col.nullable - } for col in result.columns - ] + "nullable": col.nullable, + } + for col in result.columns + ], } - + return {"schema_result": schema_result} + # Define the graph graph = ( - StateGraph(State, config_schema=Configuration) + lgg.StateGraph(State, config_schema=Configuration) .add_node("analyze_raw_data", analyze_raw_data) .add_node("parse_schema", parse_schema) .add_node("call_model", call_model) @@ -155,4 +195,4 @@ async def parse_schema(state: State, config: RunnableConfig) -> Dict[str, Any]: .add_edge("analyze_raw_data", "parse_schema") .add_edge("parse_schema", "call_model") .compile(name="AutoEDA Agent Graph") -) \ No newline at end of file +) diff --git a/langgraph/src/agent/raw_data_analyzer.py b/langgraph/src/agent/raw_data_analyzer.py index 311895b95..ccce1cac2 100644 --- a/langgraph/src/agent/raw_data_analyzer.py +++ b/langgraph/src/agent/raw_data_analyzer.py @@ -12,29 +12,33 @@ """ import argparse +import dataclasses import json import logging -from dataclasses import dataclass -from pathlib import Path -from typing import Any, Dict, List, Optional, Union +import pathlib +import warnings +from typing import Any, Dict, List, Optional -import numpy as np import pandas as pd -import warnings -import helpers.hdbg as hdbg import helpers.hio as hio -import helpers.hpandas as hpandas # Configure logging. logging.basicConfig(level=logging.INFO) _LOG = logging.getLogger(__name__) -@dataclass + +# ############################################################################# +# ColumnAnalysis +# ############################################################################# + + +@dataclasses.dataclass class ColumnAnalysis: """ Represent analysis results for a single column. """ + name: str data_type: str nullable: bool @@ -49,11 +53,17 @@ class ColumnAnalysis: format_hint: Optional[str] = None -@dataclass +# ############################################################################# +# DataAnalysisResult +# ############################################################################# + + +@dataclasses.dataclass class DataAnalysisResult: """ Represent the complete data analysis result. """ + file_path: str total_rows: int total_columns: int @@ -63,10 +73,15 @@ class DataAnalysisResult: error_message: Optional[str] = None +# ############################################################################# +# RawDataAnalyzer +# ############################################################################# + + class RawDataAnalyzer: """ Analyze raw data files and generate schema definitions. - + Supported formats: - CSV files - JSON files @@ -83,13 +98,17 @@ def __init__( ) -> None: """ Initialize the raw data analyzer. - - :param sample_size: maximum number of rows to sample for analysis - :param max_unique_values: maximum unique values to consider for enum types - :param datetime_formats: list of datetime formats to try for detection + + :param sample_size: maximum number of rows to sample for + analysis + :param max_unique_values: maximum unique values to consider for + enum types + :param datetime_formats: list of datetime formats to try for + detection """ self.sample_size = sample_size self.max_unique_values = max_unique_values + self.datetime_threshold = 0.8 # 80% success rate for datetime detection self.datetime_formats = datetime_formats or [ "%Y-%m-%d", "%Y-%m-%d %H:%M:%S", @@ -104,24 +123,20 @@ def __init__( def analyze_file(self, file_path: str) -> DataAnalysisResult: """ Analyze a data file and generate schema information. - + :param file_path: path to the data file to analyze :return: analysis results with suggested schema """ try: - path = Path(file_path) - + path = pathlib.Path(file_path) if not path.exists(): error_msg = f"Data file not found: {file_path}" return self._create_error_result(file_path, error_msg) - if not self._is_supported_format(path.suffix): error_msg = f"Unsupported file format: {path.suffix}" return self._create_error_result(file_path, error_msg) - # Load data based on file format. df = self._load_data(file_path) - # Sample data if it's too large. if len(df) > self.sample_size: _LOG.info( @@ -130,22 +145,17 @@ def analyze_file(self, file_path: str) -> DataAnalysisResult: len(df), ) df = df.sample(n=self.sample_size, random_state=42) - # Analyze each column. columns_analysis = self._analyze_columns(df) - # Generate suggested schema. suggested_schema = self._generate_schema(columns_analysis, df) - # Create analysis metadata. analysis_metadata = self._create_analysis_metadata(file_path, df) - _LOG.info( "Successfully analyzed %d columns from %s", len(columns_analysis), file_path, ) - return DataAnalysisResult( file_path=file_path, total_rows=len(df), @@ -154,8 +164,7 @@ def analyze_file(self, file_path: str) -> DataAnalysisResult: suggested_schema=suggested_schema, analysis_metadata=analysis_metadata, ) - - except Exception as e: + except (IOError, ValueError, pd.errors.ParserError) as e: _LOG.error("Error analyzing file %s: %s", file_path, e) error_msg = f"Failed to analyze file: {str(e)}" return self._create_error_result(file_path, error_msg) @@ -169,27 +178,30 @@ def save_schema( ) -> None: """ Save the generated schema to a JSON file. - + :param analysis_result: analysis result containing the schema :param output_path: path where to save the schema.json file - :param include_analysis_metadata: whether to include analysis metadata + :param include_analysis_metadata: whether to include analysis + metadata """ try: schema_data = analysis_result.suggested_schema.copy() - + if include_analysis_metadata: - schema_data["_analysis_metadata"] = analysis_result.analysis_metadata - + schema_data["_analysis_metadata"] = ( + analysis_result.analysis_metadata + ) + # Ensure output directory exists. - output_dir = Path(output_path).parent + output_dir = pathlib.Path(output_path).parent hio.create_dir(str(output_dir), incremental=True) - + # Save schema to file. with open(output_path, "w", encoding="utf-8") as f: json.dump(schema_data, f, indent=2, default=str) - + _LOG.info("Schema saved to %s", output_path) - + except Exception as e: _LOG.error("Error saving schema to %s: %s", output_path, e) raise @@ -197,13 +209,12 @@ def save_schema( def _load_data(self, file_path: str) -> pd.DataFrame: """ Load data from file based on format. - + :param file_path: path to the data file :return: loaded DataFrame """ - path = Path(file_path) + path = pathlib.Path(file_path) file_extension = path.suffix.lower() - if file_extension == ".csv": # Try different encodings and separators. for encoding in ["utf-8", "latin-1", "cp1252"]: @@ -215,45 +226,38 @@ def _load_data(self, file_path: str) -> pd.DataFrame: sep=sep, nrows=self.sample_size, ) - if len(df.columns) > 1: # Good indicator of correct separator. + # Good indicator of correct separator. + if len(df.columns) > 1: _LOG.debug( "Successfully loaded CSV with encoding=%s, sep='%s'", encoding, sep, ) return df - except Exception: + except (ValueError, TypeError, pd.errors.ParserError): continue - # Fallback to default pandas behavior. return pd.read_csv(file_path, nrows=self.sample_size) - - elif file_extension == ".json": + if file_extension == ".json": return pd.read_json(file_path, lines=True, nrows=self.sample_size) - - elif file_extension == ".parquet": + if file_extension == ".parquet": return pd.read_parquet(file_path) - - elif file_extension in [".xlsx", ".xls"]: + if file_extension in [".xlsx", ".xls"]: return pd.read_excel(file_path, nrows=self.sample_size) - - else: - raise ValueError(f"Unsupported file format: {file_extension}") + raise ValueError(f"Unsupported file format: {file_extension}") def _analyze_columns(self, df: pd.DataFrame) -> List[ColumnAnalysis]: """ Analyze each column in the DataFrame. - + :param df: DataFrame to analyze :return: list of column analysis results """ columns_analysis = [] - for col_name in df.columns: col_data = df[col_name] analysis = self._analyze_single_column(col_name, col_data) columns_analysis.append(analysis) - return columns_analysis def _analyze_single_column( @@ -263,7 +267,7 @@ def _analyze_single_column( ) -> ColumnAnalysis: """ Analyze a single column. - + :param col_name: name of the column :param col_data: column data as pandas Series :return: column analysis result @@ -272,22 +276,22 @@ def _analyze_single_column( null_count = col_data.isnull().sum() unique_count = col_data.nunique() nullable = null_count > 0 - + # Sample non-null values. non_null_data = col_data.dropna() sample_values = ( non_null_data.head(5).tolist() if len(non_null_data) > 0 else [] ) - + # Infer data type. data_type, format_hint = self._infer_data_type(non_null_data) - + # Calculate statistics for numeric columns. min_value = None max_value = None mean_value = None std_value = None - + if data_type in ["integer", "float"] and len(non_null_data) > 0: try: numeric_data = pd.to_numeric(non_null_data, errors="coerce") @@ -295,14 +299,14 @@ def _analyze_single_column( max_value = float(numeric_data.max()) mean_value = float(numeric_data.mean()) std_value = float(numeric_data.std()) - except Exception: + except (ValueError, TypeError): pass - + # Detect patterns for string columns. pattern = None if data_type == "string" and len(non_null_data) > 0: pattern = self._detect_pattern(non_null_data) - + return ColumnAnalysis( name=col_name, data_type=data_type, @@ -321,13 +325,14 @@ def _analyze_single_column( def _infer_data_type(self, data: pd.Series) -> tuple[str, Optional[str]]: """ Infer the data type of a column. - + :param data: column data (non-null values) :return: tuple of (data_type, format_hint) """ + result_type = "string" + format_hint = None if len(data) == 0: - return "string", None - + return result_type, format_hint # Check for boolean. if data.dtype == bool or set(data.astype(str).str.lower()) <= { "true", @@ -341,40 +346,47 @@ def _infer_data_type(self, data: pd.Series) -> tuple[str, Optional[str]]: "1", "0", }: - return "boolean", None - + result_type = "boolean" # Check for integer. - if data.dtype in ["int64", "int32", "int16", "int8"]: - return "integer", None - + elif data.dtype in ["int64", "int32", "int16", "int8"]: + result_type = "integer" # Check for float. - if data.dtype in ["float64", "float32"]: - return "float", None - - # Try to convert to numeric. - try: - numeric_data = pd.to_numeric(data, errors="coerce") - if not numeric_data.isnull().all(): - # Check if all values are integers. - if numeric_data.fillna(0).apply(lambda x: x.is_integer()).all(): - return "integer", None + elif data.dtype in ["float64", "float32"]: + result_type = "float" + else: + # Try to convert to numeric. + try: + numeric_data = pd.to_numeric(data, errors="coerce") + if not numeric_data.isnull().all(): + # Check if all values are integers. + if ( + numeric_data.fillna(0) + .apply(lambda x: x.is_integer()) + .all() + ): + result_type = "integer" + else: + result_type = "float" else: - return "float", None - except Exception: - pass - - # Check for datetime. - datetime_type, format_hint = self._check_datetime(data) - if datetime_type: - return datetime_type, format_hint - - # Default to string. - return "string", None - - def _check_datetime(self, data: pd.Series) -> tuple[Optional[str], Optional[str]]: + # Check for datetime. + datetime_type, datetime_format = self._check_datetime(data) + if datetime_type: + result_type = datetime_type + format_hint = datetime_format + except (ValueError, TypeError): + # Check for datetime as fallback. + datetime_type, datetime_format = self._check_datetime(data) + if datetime_type: + result_type = datetime_type + format_hint = datetime_format + return result_type, format_hint + + def _check_datetime( + self, data: pd.Series + ) -> tuple[Optional[str], Optional[str]]: """ Check if data represents datetime values with improved detection. - + :param data: column data to check :return: tuple of (datetime_type, format_hint) """ @@ -384,54 +396,53 @@ def _check_datetime(self, data: pd.Series) -> tuple[Optional[str], Optional[str] return None, None except (ValueError, TypeError): pass - # Sample a subset for testing. sample_data = data.head(min(100, len(data))) - # Try specific datetime formats first. for fmt in self.datetime_formats: try: with warnings.catch_warnings(): warnings.simplefilter("ignore") - parsed = pd.to_datetime(sample_data, format=fmt, errors="coerce") - + parsed = pd.to_datetime( + sample_data, format=fmt, errors="coerce" + ) # Check success rate. - success_rate = (1 - parsed.isnull().sum() / len(sample_data)) + success_rate = 1 - parsed.isnull().sum() / len(sample_data) if success_rate >= self.datetime_threshold: # Determine if it's date or datetime. non_null_parsed = parsed.dropna() if len(non_null_parsed) > 0: # Check if all times are midnight (date-only). - if all(t.time() == non_null_parsed.iloc[0].time() for t in non_null_parsed.head(10)): + if all( + t.time() == non_null_parsed.iloc[0].time() + for t in non_null_parsed.head(10) + ): return "date", fmt - else: - return "datetime", fmt - except Exception: + return "datetime", fmt + except (ValueError, TypeError, AttributeError): continue - # Try pandas automatic parsing as last resort. try: with warnings.catch_warnings(): warnings.simplefilter("ignore") - parsed = pd.to_datetime(sample_data, errors="coerce", infer_datetime_format=True) - - success_rate = (1 - parsed.isnull().sum() / len(sample_data)) + parsed = pd.to_datetime( + sample_data, errors="coerce", infer_datetime_format=True + ) + success_rate = 1 - parsed.isnull().sum() / len(sample_data) if success_rate >= self.datetime_threshold: return "datetime", "auto" - except Exception: + except (ValueError, TypeError, AttributeError): pass - return None, None def _detect_pattern(self, data: pd.Series) -> Optional[str]: """ Detect common patterns in string data. - + :param data: string column data :return: detected pattern or None """ str_data = data.astype(str) - # Check for common patterns. patterns = { "email": r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$", @@ -440,11 +451,9 @@ def _detect_pattern(self, data: pd.Series) -> Optional[str]: "uuid": r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$", "ip_address": r"^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$", } - for pattern_name, pattern_regex in patterns.items(): if str_data.str.match(pattern_regex, case=False).mean() > 0.8: return pattern_name - return None def _generate_schema( @@ -454,31 +463,31 @@ def _generate_schema( ) -> Dict[str, Any]: """ Generate JSON Schema from column analysis. - + :param columns_analysis: list of analyzed columns :param df: original DataFrame :return: JSON Schema dictionary """ properties = {} required_fields = [] - + for col in columns_analysis: col_schema = { "type": self._map_to_json_schema_type(col.data_type), "description": f"Column '{col.name}' with {col.unique_count} unique values", } - + # Add format hint if available. if col.format_hint and col.format_hint != "auto": col_schema["format"] = col.format_hint - + # Add constraints for numeric fields. if col.data_type in ["integer", "float"]: if col.min_value is not None: col_schema["minimum"] = col.min_value if col.max_value is not None: col_schema["maximum"] = col.max_value - + # Add enum for low-cardinality categorical fields. if ( col.data_type == "string" @@ -487,33 +496,33 @@ def _generate_schema( ): unique_values = df[col.name].dropna().unique().tolist() col_schema["enum"] = unique_values[:50] # Limit enum size. - + # Add pattern if detected. if col.pattern: col_schema["pattern"] = col.pattern - + properties[col.name] = col_schema - + # Mark as required if no null values. if not col.nullable: required_fields.append(col.name) - + schema = { "type": "object", "title": "Generated Schema", "description": f"Auto-generated schema for data with {len(columns_analysis)} columns", "properties": properties, } - + if required_fields: schema["required"] = required_fields - + return schema def _map_to_json_schema_type(self, data_type: str) -> str: """ Map internal data types to JSON Schema types. - + :param data_type: internal data type :return: JSON Schema type """ @@ -536,7 +545,7 @@ def _create_analysis_metadata( ) -> Dict[str, Any]: """ Create metadata about the analysis process. - + :param file_path: path to analyzed file :param df: analyzed DataFrame :return: analysis metadata @@ -548,7 +557,7 @@ def _create_analysis_metadata( "sample_size": min(len(df), self.sample_size), "total_rows_analyzed": len(df), "total_columns_analyzed": len(df.columns), - "file_size_bytes": Path(file_path).stat().st_size, + "file_size_bytes": pathlib.Path(file_path).stat().st_size, "analysis_settings": { "max_unique_values": self.max_unique_values, "datetime_formats": self.datetime_formats, @@ -558,7 +567,7 @@ def _create_analysis_metadata( def _is_supported_format(self, file_extension: str) -> bool: """ Check if file format is supported. - + :param file_extension: file extension to check :return: whether the format is supported """ @@ -571,7 +580,7 @@ def _create_error_result( ) -> DataAnalysisResult: """ Create a DataAnalysisResult with error information. - + :param file_path: path to the file that caused the error :param error_message: error message to include :return: DataAnalysisResult object with error details @@ -587,11 +596,12 @@ def _create_error_result( ) -def main(): - parser = argparse.ArgumentParser(description="Analyze raw data files and generate schema information.") +def main() -> None: + parser = argparse.ArgumentParser( + description="Analyze raw data files and generate schema information." + ) parser.add_argument("file", type=str, help="Path to the data file to analyze") args = parser.parse_args() - analyzer = RawDataAnalyzer() result = analyzer.analyze_file(args.file) if result.error_message: @@ -602,19 +612,20 @@ def main(): _LOG.info("Total columns: %d", result.total_columns) _LOG.info("Columns:") for col in result.columns: - _LOG.info(" - %s: %s, nullable=%s", col.name, col.data_type, col.nullable) + _LOG.info( + " - %s: %s, nullable=%s", col.name, col.data_type, col.nullable + ) _LOG.info("Suggested schema: %s", result.suggested_schema) _LOG.info("Metadata: %s", result.analysis_metadata) - - # Save suggested schema to JSON file + # Save suggested schema to JSON file. schema_path = args.file + ".schema.json" try: - with open(schema_path, "w") as f: + with open(schema_path, "w", encoding="utf-8") as f: json.dump(result.suggested_schema, f, indent=2) _LOG.info("Saved schema to %s", schema_path) - except Exception as e: + except (IOError, TypeError, ValueError) as e: _LOG.error("Failed to save schema to %s: %s", schema_path, e) + if __name__ == "__main__": main() - diff --git a/langgraph/src/agent/schema_parser.py b/langgraph/src/agent/schema_parser.py index 9f3d08d09..109885926 100755 --- a/langgraph/src/agent/schema_parser.py +++ b/langgraph/src/agent/schema_parser.py @@ -12,14 +12,13 @@ """ import argparse +import dataclasses import json import logging -import os -from dataclasses import dataclass -from pathlib import Path +import pathlib from typing import Any, Dict, List, Optional -import yaml +import yaml # type: ignore[import-untyped] import helpers.hdbg as hdbg import helpers.hio as hio @@ -29,11 +28,17 @@ _LOG = logging.getLogger(__name__) -@dataclass +# ############################################################################# +# ColumnInfo +# ############################################################################# + + +@dataclasses.dataclass class ColumnInfo: """ Represent information about a single column. """ + name: str data_type: str required: bool = False @@ -53,11 +58,17 @@ def __post_init__(self) -> None: self.metadata = {} -@dataclass +# ############################################################################# +# ParsedSchema +# ############################################################################# + + +@dataclasses.dataclass class ParsedSchema: """ Represent the complete parsed schema result. """ + columns: List[ColumnInfo] raw_schema: Dict[str, Any] schema_type: str @@ -75,10 +86,15 @@ def __post_init__(self) -> None: self.optional_columns = self.total_columns - self.required_columns +# ############################################################################# +# SchemaParser +# ############################################################################# + + class SchemaParser: """ Parse various schema formats and extract column information. - + Supported formats: - JSON Schema - Custom column definitions @@ -94,9 +110,11 @@ def __init__( ) -> None: """ Initialize the schema parser. - - :param include_metadata: whether to include metadata in parsed results - :param output_format: output format, either "detailed" or "simple" + + :param include_metadata: whether to include metadata in parsed + results + :param output_format: output format, either "detailed" or + "simple" """ hdbg.dassert_in(output_format, ["detailed", "simple"]) self.include_metadata = include_metadata @@ -106,27 +124,22 @@ def __init__( def parse_file(self, file_path: str) -> ParsedSchema: """ Parse schema from a file. - + :param file_path: path to the schema file :return: parsed schema object with results """ try: - path = Path(file_path) - + path = pathlib.Path(file_path) if not path.exists(): error_msg = f"Schema file not found: {file_path}" return self._create_error_result(error_msg) - if not self._is_supported_format(path.suffix): error_msg = f"Unsupported file format: {path.suffix}" return self._create_error_result(error_msg) - # Read file content. content = hio.from_file(str(path)) - - return self.parse_content(content, str(path)) - - except Exception as e: + return self.parse_content(content, source=str(path)) + except (FileNotFoundError, IOError, OSError, ValueError, TypeError) as e: _LOG.error("Error parsing file %s: %s", file_path, e) error_msg = f"Failed to parse file: {str(e)}" return self._create_error_result(error_msg) @@ -139,7 +152,7 @@ def parse_content( ) -> ParsedSchema: """ Parse schema from string content. - + :param content: schema content as string :param source: source identifier for logging :return: parsed schema object with results @@ -148,13 +161,13 @@ def parse_content( schema_data = self._parse_schema_content(content) schema_type = self._detect_schema_type(schema_data) columns = self._extract_columns(schema_data, schema_type) - + _LOG.info( "Successfully parsed %d columns from %s", len(columns), source, ) - + return ParsedSchema( columns=columns, raw_schema=schema_data, @@ -163,8 +176,14 @@ def parse_content( required_columns=0, # Will be calculated in __post_init__. optional_columns=0, # Will be calculated in __post_init__. ) - - except Exception as e: + + except ( + json.JSONDecodeError, + yaml.YAMLError, + ValueError, + KeyError, + TypeError, + ) as e: _LOG.error("Error parsing content from %s: %s", source, e) error_msg = f"Failed to parse content: {str(e)}" return self._create_error_result(error_msg) @@ -172,7 +191,7 @@ def parse_content( def to_dict(self, parsed_schema: ParsedSchema) -> Dict[str, Any]: """ Convert ParsedSchema to dictionary format. - + :param parsed_schema: parsed schema object to convert :return: dictionary representation of the schema """ @@ -203,7 +222,7 @@ def to_dict(self, parsed_schema: ParsedSchema) -> Dict[str, Any]: def to_dataframe_schema(self, parsed_schema: ParsedSchema) -> Dict[str, str]: """ Convert to simple column_name: data_type mapping for pandas. - + :param parsed_schema: parsed schema object to convert :return: simple mapping of column names to data types """ @@ -212,44 +231,47 @@ def to_dataframe_schema(self, parsed_schema: ParsedSchema) -> Dict[str, str]: def _parse_schema_content(self, content: str) -> Dict[str, Any]: """ Parse content as JSON or YAML. - + :param content: raw content string to parse :return: parsed data as dictionary """ content = content.strip() - # Try JSON first. try: - return json.loads(content) + parsed_data = json.loads(content) + if not isinstance(parsed_data, dict): + raise ValueError("Schema must be a JSON object/dictionary") + return parsed_data except json.JSONDecodeError: pass - # Try YAML. try: - return yaml.safe_load(content) + parsed_data = yaml.safe_load(content) + if not isinstance(parsed_data, dict): + raise ValueError("Schema must be a YAML object/dictionary") + return parsed_data except yaml.YAMLError as e: - raise ValueError(f"Invalid JSON/YAML format: {e}") + raise ValueError(f"Invalid JSON/YAML format: {e}") from e def _detect_schema_type(self, schema_data: Dict[str, Any]) -> str: """ Detect the type of schema format. - + :param schema_data: parsed schema data :return: detected schema type """ if "properties" in schema_data and "type" in schema_data: return "json_schema" - elif "columns" in schema_data: + if "columns" in schema_data: return "custom_columns" - elif "fields" in schema_data: + if "fields" in schema_data: return "fields_format" - elif any( + if any( isinstance(v, dict) and ("type" in v or "dataType" in v) for v in schema_data.values() ): return "generic_properties" - else: - return "unknown" + return "unknown" def _extract_columns( self, @@ -258,7 +280,7 @@ def _extract_columns( ) -> List[ColumnInfo]: """ Extract column information based on schema type. - + :param schema_data: parsed schema data :param schema_type: detected schema type :return: list of column information objects @@ -270,7 +292,7 @@ def _extract_columns( "generic_properties": self._extract_from_generic_format, "unknown": self._extract_from_unknown_format, } - + extractor = extractors.get(schema_type, self._extract_from_unknown_format) return extractor(schema_data) @@ -280,18 +302,18 @@ def _extract_from_json_schema( ) -> List[ColumnInfo]: """ Extract columns from JSON Schema format. - + :param schema_data: JSON schema data :return: list of column information objects """ columns = [] properties = schema_data.get("properties", {}) required_fields = set(schema_data.get("required", [])) - + for column_name, column_info in properties.items(): constraints = {} metadata = {} - + # Extract constraints. for key in [ "minimum", @@ -303,7 +325,7 @@ def _extract_from_json_schema( ]: if key in column_info: constraints[key] = column_info[key] - + # Extract metadata if enabled. if self.include_metadata: excluded_keys = [ @@ -317,11 +339,9 @@ def _extract_from_json_schema( "enum", ] metadata = { - k: v - for k, v in column_info.items() - if k not in excluded_keys + k: v for k, v in column_info.items() if k not in excluded_keys } - + column = ColumnInfo( name=column_name, data_type=self._map_json_schema_type( @@ -334,9 +354,9 @@ def _extract_from_json_schema( constraints=constraints, metadata=metadata, ) - + columns.append(column) - + return columns def _extract_from_custom_format( @@ -345,20 +365,20 @@ def _extract_from_custom_format( ) -> List[ColumnInfo]: """ Extract columns from custom columns format. - + :param schema_data: custom format schema data :return: list of column information objects """ columns = [] columns_data = schema_data.get("columns", []) - + for column_info in columns_data: if not isinstance(column_info, dict): continue - + constraints = column_info.get("constraints", {}) metadata = {} - + if self.include_metadata: excluded_keys = [ "name", @@ -370,11 +390,9 @@ def _extract_from_custom_format( "constraints", ] metadata = { - k: v - for k, v in column_info.items() - if k not in excluded_keys + k: v for k, v in column_info.items() if k not in excluded_keys } - + column = ColumnInfo( name=column_info.get("name", ""), data_type=self._normalize_data_type( @@ -387,9 +405,9 @@ def _extract_from_custom_format( constraints=constraints, metadata=metadata, ) - + columns.append(column) - + return columns def _extract_from_fields_format( @@ -398,20 +416,20 @@ def _extract_from_fields_format( ) -> List[ColumnInfo]: """ Extract columns from fields format. - + :param schema_data: fields format schema data :return: list of column information objects """ columns = [] fields_data = schema_data.get("fields", []) - + for field_info in fields_data: if not isinstance(field_info, dict): continue - + constraints = field_info.get("constraints", {}) metadata = {} - + if self.include_metadata: excluded_keys = [ "name", @@ -421,11 +439,9 @@ def _extract_from_fields_format( "constraints", ] metadata = { - k: v - for k, v in field_info.items() - if k not in excluded_keys + k: v for k, v in field_info.items() if k not in excluded_keys } - + column = ColumnInfo( name=field_info.get("name", ""), data_type=self._normalize_data_type( @@ -437,9 +453,9 @@ def _extract_from_fields_format( constraints=constraints, metadata=metadata, ) - + columns.append(column) - + return columns def _extract_from_generic_format( @@ -448,19 +464,19 @@ def _extract_from_generic_format( ) -> List[ColumnInfo]: """ Extract columns from generic format. - + :param schema_data: generic format schema data :return: list of column information objects """ columns = [] - + for key, value in schema_data.items(): if not isinstance(value, dict): continue - + if "type" not in value and "dataType" not in value: continue - + metadata = {} if self.include_metadata: excluded_keys = [ @@ -473,7 +489,7 @@ def _extract_from_generic_format( metadata = { k: v for k, v in value.items() if k not in excluded_keys } - + column = ColumnInfo( name=key, data_type=self._normalize_data_type( @@ -484,9 +500,9 @@ def _extract_from_generic_format( nullable=value.get("nullable", True), metadata=metadata, ) - + columns.append(column) - + return columns def _extract_from_unknown_format( @@ -495,12 +511,12 @@ def _extract_from_unknown_format( ) -> List[ColumnInfo]: """ Extract columns from unknown format by making best guesses. - + :param schema_data: unknown format schema data :return: list of column information objects """ columns = [] - + # Try to extract any key-value pairs as potential columns. for key, value in schema_data.items(): if isinstance(value, str): @@ -511,13 +527,13 @@ def _extract_from_unknown_format( description="Inferred from key-value pair", ) columns.append(column) - + return columns def _map_json_schema_type(self, json_type: str) -> str: """ Map JSON Schema types to standard data types. - + :param json_type: JSON schema type string :return: normalized data type """ @@ -535,15 +551,13 @@ def _map_json_schema_type(self, json_type: str) -> str: def _normalize_data_type(self, data_type: str) -> str: """ Normalize various data type representations. - + :param data_type: raw data type string :return: normalized data type """ if not isinstance(data_type, str): return "string" - data_type = data_type.lower().strip() - type_mapping = { # String types. "str": "string", @@ -581,13 +595,12 @@ def _normalize_data_type(self, data_type: str) -> str: "blob": "binary", "binary": "binary", } - return type_mapping.get(data_type, data_type) def _is_supported_format(self, file_extension: str) -> bool: """ Check if file format is supported. - + :param file_extension: file extension to check :return: whether the format is supported """ @@ -596,7 +609,7 @@ def _is_supported_format(self, file_extension: str) -> bool: def _create_error_result(self, error_message: str) -> ParsedSchema: """ Create a ParsedSchema with error information. - + :param error_message: error message to include :return: ParsedSchema object with error details """ @@ -623,7 +636,7 @@ def parse_schema_file( ) -> ParsedSchema: """ Parse a schema file using default settings. - + :param file_path: path to schema file :param include_metadata: whether to include metadata :return: parsed schema object @@ -639,7 +652,7 @@ def parse_schema_content( ) -> ParsedSchema: """ Parse schema content using default settings. - + :param content: schema content as string :param include_metadata: whether to include metadata :return: parsed schema object @@ -648,19 +661,21 @@ def parse_schema_content( return parser.parse_content(content) -def main(): - parser = argparse.ArgumentParser(description="Parse a schema file and log the results.") - parser.add_argument("schema_file", type=str, help="Path to the schema file (JSON)") +def main() -> None: + parser = argparse.ArgumentParser( + description="Parse a schema file and log the results." + ) + parser.add_argument( + "schema_file", type=str, help="Path to the schema file (JSON)" + ) args = parser.parse_args() - # Read schema content from file try: - with open(args.schema_file, "r") as f: + with open(args.schema_file, "r", encoding="utf-8") as f: schema_content = f.read() - except Exception as e: + except (FileNotFoundError, IOError, OSError) as e: _LOG.error("Failed to read schema file %s: %s", args.schema_file, e) return - result = parse_schema_content(schema_content) if result.error_message: _LOG.error("Error: %s", result.error_message) @@ -671,7 +686,10 @@ def main(): _LOG.info("Schema type: %s", result.schema_type) _LOG.info("Columns:") for col in result.columns: - _LOG.info(" - %s: %s (required: %s)", col.name, col.data_type, col.required) + _LOG.info( + " - %s: %s (required: %s)", col.name, col.data_type, col.required + ) + if __name__ == "__main__": - main() \ No newline at end of file + main() From 5d425b201a764b8cf284b34f7e8935b5c409fae5 Mon Sep 17 00:00:00 2001 From: maddy Date: Fri, 8 Aug 2025 18:54:51 -0400 Subject: [PATCH 3/4] Removed test caused some problems MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pre-commit checks: All checks passed ✅ --- langgraph/test.py | 189 ---------------------------------------------- 1 file changed, 189 deletions(-) delete mode 100644 langgraph/test.py diff --git a/langgraph/test.py b/langgraph/test.py deleted file mode 100644 index 353d0fd3b..000000000 --- a/langgraph/test.py +++ /dev/null @@ -1,189 +0,0 @@ -"""Test script for AutoEDA Agent LangGraph.""" - -import asyncio -import json -from pathlib import Path - -from langgraph_sdk import get_client - -client = get_client(url="http://localhost:2024") - -async def test_autoeda_agent(): - """Test the AutoEDA agent with sample data.""" - - # Test data file path (absolute path to ensure it's found) - test_file_path = "/home/maddev/src/helpers1/langgraph/src/agent/data.csv" - - # Sample schema content for testing - sample_schema = { - "type": "object", - "properties": { - "user_id": { - "type": "integer", - "description": "Unique identifier for user" - }, - "username": { - "type": "string", - "description": "User's login name" - }, - "email": { - "type": "string", - "format": "email", - "description": "User's email address" - }, - "age": { - "type": "integer", - "minimum": 0, - "maximum": 150 - }, - "is_active": { - "type": "boolean", - "default": True - } - }, - "required": ["user_id", "username", "email"] - } - - print("Testing AutoEDA Agent...") - print(f"File path: {test_file_path}") - print("=" * 50) - - try: - async for chunk in client.runs.stream( - None, # Threadless run - "agent", # Name of assistant defined in langgraph.json - input={ - "file_path": test_file_path, - "schema_content": json.dumps(sample_schema, indent=2), - "changeme": "Analyze this dataset for AutoEDA insights" - }, - config={ - "configurable": { - "my_configurable_param": "AutoEDA_Analysis", - "openai_model": "gpt-3.5-turbo" - } - } - ): - print(f"Event: {chunk.event}") - if hasattr(chunk, 'node') and chunk.node: - print(f"Node: {chunk.node}") - - if chunk.data: - if isinstance(chunk.data, dict): - # Pretty print the data - for key, value in chunk.data.items(): - if key == "changeme" and isinstance(value, str): - print(f"\nAutoEDA Output:\n{value}") - elif key == "raw_data_result": - print(f"\nRaw Data Analysis Result:") - if isinstance(value, dict): - if "error" in value: - print(f" Error: {value['error']}") - else: - print(f" File: {value.get('file_path', 'unknown')}") - print(f" Rows: {value.get('total_rows', 0)}") - print(f" Columns: {value.get('total_columns', 0)}") - else: - print(f" {value}") - elif key == "schema_result": - print(f"\nSchema Analysis Result:") - if isinstance(value, dict): - if "error" in value: - print(f" Error: {value['error']}") - else: - print(f" Total columns: {value.get('total_columns', 0)}") - print(f" Required: {value.get('required_columns', 0)}") - print(f" Optional: {value.get('optional_columns', 0)}") - print(f" Schema type: {value.get('schema_type', 'unknown')}") - else: - print(f" {value}") - else: - print(f"{key}: {value}") - else: - print(f"Data: {chunk.data}") - print("-" * 30) - - except Exception as e: - print(f"Error running AutoEDA agent: {e}") - import traceback - traceback.print_exc() - -async def test_error_handling(): - """Test error handling with invalid inputs.""" - - print("\nTesting error handling...") - print("=" * 50) - - try: - async for chunk in client.runs.stream( - None, - "agent", - input={ - "file_path": "nonexistent_file.csv", - "schema_content": "invalid json content", - "changeme": "Test error handling" - }, - config={ - "configurable": { - "my_configurable_param": "Error_Test", - "openai_model": "gpt-3.5-turbo" - } - } - ): - print(f"Event: {chunk.event}") - if chunk.data: - print(f"Data: {chunk.data}") - print("-" * 30) - - except Exception as e: - print(f"Expected error caught: {e}") - -async def test_direct_nodes(): - """Test the nodes directly to verify they work.""" - print("\nTesting nodes directly...") - print("=" * 50) - - try: - # Import the graph and nodes - from src.agent.graph import analyze_raw_data, parse_schema, State - from langchain_core.runnables import RunnableConfig - - # Create test state - state = State( - file_path="/home/maddev/src/helpers1/langgraph/src/agent/data.csv", - schema_content=json.dumps({ - "type": "object", - "properties": { - "user_id": {"type": "integer"}, - "username": {"type": "string"}, - "email": {"type": "string"} - }, - "required": ["user_id", "username"] - }) - ) - - config = RunnableConfig(configurable={}) - - # Test raw data analysis - print("Testing raw data analysis...") - raw_result = await analyze_raw_data(state, config) - print(f"Raw data result: {raw_result}") - - # Test schema parsing - print("\nTesting schema parsing...") - schema_result = await parse_schema(state, config) - print(f"Schema result: {schema_result}") - - except Exception as e: - print(f"Error in direct node testing: {e}") - import traceback - traceback.print_exc() - -async def main(): - """Run all tests.""" - await test_autoeda_agent() - await test_error_handling() - await test_direct_nodes() - -if __name__ == "__main__": - asyncio.run(main()) From b2bf15a771599f249da9bba3668a537c03527b44 Mon Sep 17 00:00:00 2001 From: maddy Date: Fri, 8 Aug 2025 19:38:43 -0400 Subject: [PATCH 4/4] Removed error test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pre-commit checks: All checks passed ✅ --- langgraph/openaitest.py | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 langgraph/openaitest.py diff --git a/langgraph/openaitest.py b/langgraph/openaitest.py deleted file mode 100644 index 2e0aee6d9..000000000 --- a/langgraph/openaitest.py +++ /dev/null @@ -1,13 +0,0 @@ -import openai -import os -from dotenv import load_dotenv - -load_dotenv() -openai.api_key = os.getenv("OPENAI_API_KEY") - -# New OpenAI v1.x syntax -client = openai.OpenAI(api_key=openai.api_key) -models = client.models.list() - -for model in models.data: - print(model.id) \ No newline at end of file