Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ share/python-wheels/
*.egg
MANIFEST
notebooks/
scripts/

# PyInstaller
# Usually these files are written by a python script from a template
Expand Down
15 changes: 15 additions & 0 deletions aixplain/v2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
EvaluatorConfig,
EditorConfig,
)
from .session import Session, SessionMessage, SessionMessageAttachment
from .meta_agents import Debugger, DebugResult
from .agent_progress import AgentProgressTracker, ProgressFormat
from .api_key import APIKey, APIKeyLimits, APIKeyUsageLimit, TokenType
Expand Down Expand Up @@ -48,6 +49,11 @@
EvolveType,
CodeInterpreterModel,
SplittingOptions,
SessionStatus,
RunStatus,
MessageRole,
Reaction,
AttachmentType,
)

__all__ = [
Expand All @@ -59,6 +65,10 @@
"FileUploader",
"upload_file",
"validate_file_for_upload",
# Session classes
"Session",
"SessionMessage",
"SessionMessageAttachment",
# Inspector classes
"Inspector",
"InspectorTarget",
Expand Down Expand Up @@ -111,4 +121,9 @@
"EvolveType",
"CodeInterpreterModel",
"SplittingOptions",
"SessionStatus",
"RunStatus",
"MessageRole",
"Reaction",
"AttachmentType",
]
110 changes: 56 additions & 54 deletions aixplain/v2/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,14 @@ class ConversationMessage(TypedDict):
Attributes:
role: The role of the message sender, either 'user' or 'assistant'
content: The text content of the message
attachments: Optional pre-built attachment dicts (url, name, type)
files: Optional local file paths to upload and attach
"""

role: Literal["user", "assistant"]
content: str
attachments: NotRequired[Optional[List[Dict[str, Any]]]]
files: NotRequired[Optional[List[Any]]]


def validate_history(history: List[Dict[str, Any]]) -> bool:
Expand Down Expand Up @@ -969,74 +973,72 @@ def build_run_payload(self, **kwargs: Unpack[AgentRunParams]) -> dict:

return payload

def generate_session_id(self, history: Optional[List[ConversationMessage]] = None) -> str:
"""Generate a unique session ID for agent conversations.

Creates a unique session identifier based on the agent ID and current timestamp.
If conversation history is provided, it attempts to initialize the session on the
server to enable context-aware conversations.
def create_session(
self,
name: Optional[str] = None,
history: Optional[List[ConversationMessage]] = None,
) -> "Session":
"""Create a new backend-managed session for this agent.

Args:
history: Previous conversation history. Each message should contain
'role' (either 'user' or 'assistant') and 'content' keys.
Defaults to None.
name: Optional human-readable name for the session.
history: Optional conversation history to seed the session with.
Each message must have 'role' and 'content' keys.
Messages may also include optional 'attachments'
(pre-built dicts with url/name/type) and/or 'files'
(local file paths to upload).

Returns:
str: A unique session identifier in the format "{agent_id}_{timestamp}".
Session: The created Session instance, pre-populated with
history messages when provided.

Raises:
ValueError: If the history format is invalid.
ValueError: If the agent has not been saved yet or if history
format is invalid.

Example:
>>> agent = Agent.get("my_agent_id")
>>> session_id = agent.generate_session_id()
>>> # Or with history
>>> history = [
... {"role": "user", "content": "Hello"},
... {"role": "assistant", "content": "Hi there!"}
... ]
>>> session_id = agent.generate_session_id(history=history)
>>> session = agent.create_session(
... name="My Chat",
... history=[
... {"role": "user", "content": "Analyze this",
... "files": ["/tmp/data.csv"]},
... {"role": "assistant", "content": "Here are the results..."},
... ],
... )
"""
if not self.id:
self.save(as_draft=True)
raise ValueError("Agent must be saved before creating a session. Call agent.save() first.")

if history:
validate_history(history)

timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
session_id = f"{self.id}_{timestamp}"

if not history:
return session_id

try:
# Use the existing run infrastructure to initialize the session
result = self.run_async(
query="/",
session_id=session_id,
history=history,
execution_params={
"max_tokens": 2048,
"max_iterations": 10,
"output_format": OutputFormat.TEXT.value,
"expected_output": None,
},
allow_history_and_session_id=True,
)
session = self.context.Session(agent_id=self.id, name=name)
session.save()

if history:
for message in history:
session.add_message(
role=message["role"],
content=message["content"],
attachments=message.get("attachments"),
files=message.get("files"),
)

# If we got a polling URL, poll for completion
if result.url and not result.completed:
final_result = self.sync_poll(result.url, timeout=300, wait_time=0.5)
return session

if final_result.status == ResponseStatus.SUCCESS:
return session_id
else:
logging.error(f"Session {session_id} initialization failed: {final_result}")
return session_id
else:
# Direct completion or no polling needed
return session_id
def list_sessions(self, status: Optional[str] = None) -> list:
"""List sessions for this agent.

Args:
status: Optional status filter (e.g. "active", "completed").

Returns:
List of Session instances belonging to this agent.

Raises:
ValueError: If the agent has not been saved yet.
"""
if not self.id:
raise ValueError("Agent must be saved before listing sessions. Call agent.save() first.")

except Exception as e:
logging.error(f"Failed to initialize session {session_id}: {e}")
return session_id
return self.context.Session.list(agent_id=self.id, status=status)
10 changes: 10 additions & 0 deletions aixplain/v2/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from .inspector import Inspector
from .meta_agents import Debugger
from .api_key import APIKey
from .session import Session
from . import enums


Expand All @@ -25,6 +26,7 @@
InspectorType = TypeVar("InspectorType", bound=Inspector)
DebuggerType = TypeVar("DebuggerType", bound=Debugger)
APIKeyType = TypeVar("APIKeyType", bound=APIKey)
SessionType = TypeVar("SessionType", bound=Session)


class Aixplain:
Expand All @@ -49,6 +51,7 @@ class Aixplain:
Inspector: InspectorType = None
Debugger: DebuggerType = None
APIKey: APIKeyType = None
Session: SessionType = None

Function = enums.Function
Supplier = enums.Supplier
Expand All @@ -66,6 +69,12 @@ class Aixplain:
SortOrder = enums.SortOrder
StorageType = enums.StorageType

SessionStatus = enums.SessionStatus
RunStatus = enums.RunStatus
MessageRole = enums.MessageRole
Reaction = enums.Reaction
AttachmentType = enums.AttachmentType

BACKEND_URL = "https://platform-api.aixplain.com"
BENCHMARKS_BACKEND_URL = "https://platform-api.aixplain.com"
MODELS_RUN_URL = "https://models.aixplain.com/api/v2/execute"
Expand Down Expand Up @@ -121,3 +130,4 @@ def init_resources(self) -> None:
self.Inspector = type("Inspector", (Inspector,), {"context": self})
self.Debugger = type("Debugger", (Debugger,), {"context": self})
self.APIKey = type("APIKey", (APIKey,), {"context": self})
self.Session = type("Session", (Session,), {"context": self})
48 changes: 48 additions & 0 deletions aixplain/v2/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,49 @@ class SplittingOptions(str, Enum):
LINE = "line"


class SessionStatus(str, Enum):
"""Session status values."""

ACTIVE = "active"
COMPLETED = "completed"
FAILED = "failed"
ARCHIVED = "archived"


class RunStatus(str, Enum):
"""Run status values for sessions."""

IDLE = "idle"
RUNNING = "running"
COMPLETED = "completed"


class MessageRole(str, Enum):
"""Message role in a session conversation."""

USER = "user"
ASSISTANT = "assistant"


class Reaction(str, Enum):
"""Reaction types for session messages."""

LIKE = "LIKE"
DISLIKE = "DISLIKE"


class AttachmentType(str, Enum):
"""Attachment type for session message attachments."""

TEXT = "text"
IMAGE = "image"
VIDEO = "video"
AUDIO = "audio"
DOCUMENT = "document"
CODE = "code"
UNKNOWN = "unknown"


__all__ = [
"AuthenticationScheme",
"FileType",
Expand All @@ -268,4 +311,9 @@ class SplittingOptions(str, Enum):
"CodeInterpreterModel",
"DataType",
"SplittingOptions",
"SessionStatus",
"RunStatus",
"MessageRole",
"Reaction",
"AttachmentType",
]
Loading