Skip to content

Commit 09a56cf

Browse files
authored
Merge pull request #1831 from sinaptik-ai/move_skills_to_ee
refactor move skills to ee folder
2 parents e342f17 + 67d89c7 commit 09a56cf

File tree

13 files changed

+201
-139
lines changed

13 files changed

+201
-139
lines changed

pandasai/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,31 @@
22
"""
33
PandasAI is a wrapper around a LLM to make dataframes conversational
44
"""
5+
from __future__ import annotations
56

67
import os
78
from io import BytesIO
89
from typing import Hashable, List, Optional, Union
910

1011
import pandas as pd
1112

12-
from pandasai.config import APIKeyManager, ConfigManager, SkillsManager
13+
from pandasai.config import APIKeyManager, ConfigManager
1314
from pandasai.data_loader.semantic_layer_schema import (
1415
Column,
1516
Relation,
1617
SemanticLayerSchema,
1718
Source,
1819
Transformation,
1920
)
21+
from pandasai.ee.skills import skill
22+
from pandasai.ee.skills.manager import SkillsManager
2023
from pandasai.exceptions import DatasetNotFound, InvalidConfigError
2124
from pandasai.helpers.path import (
2225
find_project_root,
2326
get_validated_dataset_path,
2427
transform_dash_to_underscore,
2528
)
2629
from pandasai.sandbox.sandbox import Sandbox
27-
from pandasai.skills import skill
2830

2931
from .agent import Agent
3032
from .data_loader.loader import DatasetLoader

pandasai/agent/base.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@ def __init__(
6565
stacklevel=2,
6666
)
6767

68+
# Transition pd dataframe to pandasai dataframe
69+
if isinstance(dfs, list):
70+
dfs = [DataFrame(df) if self.is_pd_dataframe(df) else df for df in dfs]
71+
elif self.is_pd_dataframe(dfs):
72+
dfs = DataFrame(dfs)
73+
6874
if isinstance(dfs, list):
6975
sources = [df.schema.source or df._loader.source for df in dfs]
7076
if not BaseQueryBuilder.check_compatible_sources(sources):
@@ -80,6 +86,9 @@ def __init__(
8086
self._response_parser = ResponseParser()
8187
self._sandbox = sandbox
8288

89+
def is_pd_dataframe(self, df: Union[DataFrame, VirtualDataFrame]) -> bool:
90+
return not isinstance(df, DataFrame) and isinstance(df, pd.DataFrame)
91+
8392
def chat(self, query: str, output_type: Optional[str] = None):
8493
"""
8594
Start a new chat interaction with the assistant on Dataframe.

pandasai/agent/state.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,10 @@
55
from dataclasses import dataclass, field
66
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
77

8-
from pandasai.config import Config, ConfigManager, SkillsManager
8+
from pandasai.config import Config, ConfigManager
99
from pandasai.constants import DEFAULT_CHART_DIRECTORY
1010
from pandasai.data_loader.semantic_layer_schema import is_schema_source_same
11+
from pandasai.ee.skills.manager import SkillsManager
1112
from pandasai.exceptions import InvalidConfigError
1213
from pandasai.helpers.folder import Folder
1314
from pandasai.helpers.logger import Logger

pandasai/config.py

Lines changed: 1 addition & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
import os
2-
from importlib.util import find_spec
3-
from typing import Any, Dict, List, Optional
2+
from typing import Any, Dict, Optional
43

54
from pydantic import BaseModel, ConfigDict
65

76
from pandasai.helpers.filemanager import DefaultFileManager, FileManager
87
from pandasai.llm.base import LLM
9-
from pandasai.skills import Skill
108

119

1210
class Config(BaseModel):
@@ -59,88 +57,3 @@ def set(cls, api_key: str):
5957
@classmethod
6058
def get(cls) -> Optional[str]:
6159
return cls._api_key
62-
63-
64-
class SkillsManager:
65-
"""
66-
A singleton class to manage the global skills list.
67-
"""
68-
69-
_skills: List[Skill] = []
70-
71-
@classmethod
72-
def add_skills(cls, *skills: Skill):
73-
"""
74-
Add skills to the global list of skills. If a skill with the same name
75-
already exists, raise an error.
76-
77-
Args:
78-
*skills: Variable number of skill objects to add.
79-
"""
80-
for skill in skills:
81-
if any(existing_skill.name == skill.name for existing_skill in cls._skills):
82-
raise ValueError(f"Skill with name '{skill.name}' already exists.")
83-
84-
cls._skills.extend(skills)
85-
86-
@classmethod
87-
def skill_exists(cls, name: str):
88-
"""
89-
Check if a skill with the given name exists in the global list of skills.
90-
91-
Args:
92-
name (str): The name of the skill to check.
93-
94-
Returns:
95-
bool: True if a skill with the given name exists, False otherwise.
96-
"""
97-
return any(skill.name == name for skill in cls._skills)
98-
99-
@classmethod
100-
def has_skills(cls):
101-
"""
102-
Check if there are any skills in the global list of skills.
103-
104-
Returns:
105-
bool: True if there are skills, False otherwise.
106-
"""
107-
return len(cls._skills) > 0
108-
109-
@classmethod
110-
def get_skill_by_func_name(cls, name: str):
111-
"""
112-
Get a skill by its name from the global list.
113-
114-
Args:
115-
name (str): The name of the skill to retrieve.
116-
117-
Returns:
118-
Skill or None: The skill with the given name, or None if not found.
119-
"""
120-
return next((skill for skill in cls._skills if skill.name == name), None)
121-
122-
@classmethod
123-
def get_skills(cls) -> List[Skill]:
124-
"""
125-
Get the global list of skills.
126-
127-
Returns:
128-
List[Skill]: The list of all skills.
129-
"""
130-
return cls._skills.copy()
131-
132-
@classmethod
133-
def clear_skills(cls):
134-
"""
135-
Clear all skills from the global list.
136-
"""
137-
cls._skills.clear()
138-
139-
@classmethod
140-
def __str__(cls) -> str:
141-
"""
142-
Present all skills
143-
Returns:
144-
str: String representation of all skills
145-
"""
146-
return "\n".join(str(skill) for skill in cls._skills)

pandasai/ee/LICENSE

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
The PandasAI Enterprise license (the “Enterprise License”)
2+
Copyright (c) 2024 Sinaptik GmbH
3+
4+
With regard to the PandasAI Software:
5+
6+
This software and associated documentation files (the "Software") may only be
7+
used in production, if you (and any entity that you represent) have agreed to,
8+
and are in compliance with, the PandasAI Subscription Terms of Service, available
9+
at https://pandas-ai.com/terms (the “Enterprise Terms”), or other
10+
agreement governing the use of the Software, as agreed by you and PandasAI,
11+
and otherwise have a valid PandasAI Enterprise license for the
12+
correct number of user seats. Subject to the foregoing sentence, you are free to
13+
modify this Software and publish patches to the Software. You agree that PandasAI
14+
and/or its licensors (as applicable) retain all right, title and interest in and
15+
to all such modifications and/or patches, and all such modifications and/or
16+
patches may only be used, copied, modified, displayed, distributed, or otherwise
17+
exploited with a valid PandasAI Enterprise license for the correct
18+
number of user seats. Notwithstanding the foregoing, you may copy and modify
19+
the Software for development and testing purposes, without requiring a
20+
subscription. You agree that PandasAI and/or its licensors (as applicable) retain
21+
all right, title and interest in and to all such modifications. You are not
22+
granted any other rights beyond what is expressly stated herein. Subject to the
23+
foregoing, it is forbidden to copy, merge, publish, distribute, sublicense,
24+
and/or sell the Software.
25+
26+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32+
SOFTWARE.
33+
34+
For all third party components incorporated into the PandasAI Software, those
35+
components are licensed under the original license provided by the owner of the
36+
applicable component.

pandasai/skills/__init__.py renamed to pandasai/ee/skills/__init__.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from pydantic import BaseModel, PrivateAttr
55

66

7-
class Skill(BaseModel):
7+
class SkillType(BaseModel):
88
"""Skill that takes a function usable by pandasai"""
99

1010
func: Callable[..., Any]
@@ -41,7 +41,7 @@ def __init__(
4141
)
4242
signature = f"def {name}{inspect.signature(func)}:"
4343

44-
super(Skill, self).__init__(
44+
super(SkillType, self).__init__(
4545
func=func, description=description, name=name, **kwargs
4646
)
4747
self._signature = signature
@@ -51,7 +51,7 @@ def __call__(self, *args, **kwargs) -> Any:
5151
return self.func(*args, **kwargs)
5252

5353
@classmethod
54-
def from_function(cls, func: Callable, **kwargs: Any) -> "Skill":
54+
def from_function(cls, func: Callable, **kwargs: Any) -> "SkillType":
5555
"""
5656
Creates a skill object from a function
5757
@@ -95,8 +95,8 @@ def compute_flight_prices(offers: pd.Dataframe) -> List[float]:
9595
"""
9696

9797
def _make_skill_with_name(skill_name: str) -> Callable:
98-
def _make_skill(skill_fn: Callable) -> Skill:
99-
skill_obj = Skill(
98+
def _make_skill(skill_fn: Callable) -> SkillType:
99+
skill_obj = SkillType(
100100
name=skill_name, # func.__name__ if None
101101
# when this decorator is used, the function MUST have a docstring
102102
description=skill_fn.__doc__,
@@ -105,7 +105,7 @@ def _make_skill(skill_fn: Callable) -> Skill:
105105

106106
# Automatically add the skill to the global skills manager
107107
try:
108-
from pandasai.config import SkillsManager
108+
from pandasai.ee.skills.manager import SkillsManager
109109

110110
SkillsManager.add_skills(skill_obj)
111111
except ImportError:
@@ -125,11 +125,14 @@ def _make_skill(skill_fn: Callable) -> Skill:
125125
elif not args:
126126
# Covers the case in which a function is decorated with "@skill()"
127127
# with the intended behavior of "@skill"
128-
def _func_wrapper(fn: Callable) -> Skill:
128+
def _func_wrapper(fn: Callable) -> SkillType:
129129
return _make_skill_with_name(fn.__name__)(fn)
130130

131131
return _func_wrapper
132132
else:
133133
raise ValueError(
134134
f"Too many arguments for skill decorator. Received: {len(args)}"
135135
)
136+
137+
138+
__all__ = ["skill", "SkillType"]

pandasai/ee/skills/manager.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
from typing import List
2+
3+
from pandasai.ee.skills import SkillType
4+
5+
6+
class SkillsManager:
7+
"""
8+
A singleton class to manage the global skills list.
9+
"""
10+
11+
_skills: List[SkillType] = []
12+
13+
@classmethod
14+
def add_skills(cls, *skills: SkillType):
15+
"""
16+
Add skills to the global list of skills. If a skill with the same name
17+
already exists, raise an error.
18+
19+
Args:
20+
*skills: Variable number of skill objects to add.
21+
"""
22+
for skill in skills:
23+
if any(existing_skill.name == skill.name for existing_skill in cls._skills):
24+
raise ValueError(f"Skill with name '{skill.name}' already exists.")
25+
26+
cls._skills.extend(skills)
27+
28+
@classmethod
29+
def skill_exists(cls, name: str):
30+
"""
31+
Check if a skill with the given name exists in the global list of skills.
32+
33+
Args:
34+
name (str): The name of the skill to check.
35+
36+
Returns:
37+
bool: True if a skill with the given name exists, False otherwise.
38+
"""
39+
return any(skill.name == name for skill in cls._skills)
40+
41+
@classmethod
42+
def has_skills(cls):
43+
"""
44+
Check if there are any skills in the global list of skills.
45+
46+
Returns:
47+
bool: True if there are skills, False otherwise.
48+
"""
49+
return len(cls._skills) > 0
50+
51+
@classmethod
52+
def get_skill_by_func_name(cls, name: str):
53+
"""
54+
Get a skill by its name from the global list.
55+
56+
Args:
57+
name (str): The name of the skill to retrieve.
58+
59+
Returns:
60+
Skill or None: The skill with the given name, or None if not found.
61+
"""
62+
return next((skill for skill in cls._skills if skill.name == name), None)
63+
64+
@classmethod
65+
def get_skills(cls) -> List[SkillType]:
66+
"""
67+
Get the global list of skills.
68+
69+
Returns:
70+
List[SkillType]: The list of all skills.
71+
"""
72+
return cls._skills.copy()
73+
74+
@classmethod
75+
def clear_skills(cls):
76+
"""
77+
Clear all skills from the global list.
78+
"""
79+
cls._skills.clear()
80+
81+
@classmethod
82+
def __str__(cls) -> str:
83+
"""
84+
Present all skills
85+
Returns:
86+
str: String representation of all skills
87+
"""
88+
return "\n".join(str(skill) for skill in cls._skills)

pandasai/smart_datalake/__init__.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@ def load_dfs(self, dfs: List[pd.DataFrame]):
3434
load_dfs = []
3535
for df in dfs:
3636
if isinstance(df, pd.DataFrame):
37-
load_dfs.append(DataFrame(df))
37+
load_dfs.append(
38+
DataFrame(df)
39+
if not isinstance(df, DataFrame) and isinstance(df, pd.DataFrame)
40+
else df
41+
)
3842
else:
3943
raise ValueError(
4044
"Invalid input data. We cannot convert it to a dataframe."

tests/unit_tests/skills/test_shared_template.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
import pytest
99
from jinja2 import Environment, FileSystemLoader
1010

11-
from pandasai.config import SkillsManager
12-
from pandasai.skills import Skill, skill
11+
from pandasai.ee.skills import skill
12+
from pandasai.ee.skills.manager import SkillsManager
1313

1414

1515
class TestSharedTemplate:

0 commit comments

Comments
 (0)