wizelit-sdk 0.1.23__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wizelit_sdk/__init__.py +11 -0
- wizelit_sdk/agent_wrapper/__init__.py +9 -0
- wizelit_sdk/agent_wrapper/agent_wrapper.py +615 -0
- wizelit_sdk/agent_wrapper/job.py +379 -0
- wizelit_sdk/agent_wrapper/streaming.py +206 -0
- wizelit_sdk/agent_wrapper/utils.py +45 -0
- wizelit_sdk/database.py +148 -0
- wizelit_sdk/models/__init__.py +4 -0
- wizelit_sdk/models/base.py +25 -0
- wizelit_sdk/models/job.py +72 -0
- wizelit_sdk-0.1.23.dist-info/METADATA +111 -0
- wizelit_sdk-0.1.23.dist-info/RECORD +13 -0
- wizelit_sdk-0.1.23.dist-info/WHEEL +4 -0
wizelit_sdk/database.py
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
from sqlalchemy import text
|
|
2
|
+
from sqlalchemy.ext.asyncio import (
|
|
3
|
+
create_async_engine,
|
|
4
|
+
async_sessionmaker,
|
|
5
|
+
AsyncSession,
|
|
6
|
+
AsyncEngine
|
|
7
|
+
)
|
|
8
|
+
from contextlib import asynccontextmanager
|
|
9
|
+
from typing import AsyncGenerator, Optional
|
|
10
|
+
import logging
|
|
11
|
+
import os
|
|
12
|
+
|
|
13
|
+
from wizelit_sdk.models.base import BaseModel
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
class DatabaseManager:
|
|
18
|
+
"""Singleton database manager with connection pooling."""
|
|
19
|
+
|
|
20
|
+
DATABASE_URL: str = ""
|
|
21
|
+
POOL_SIZE: int = 5
|
|
22
|
+
MAX_OVERFLOW: int = 10
|
|
23
|
+
POOL_TIMEOUT: int = 30
|
|
24
|
+
POOL_RECYCLE: int = 3600
|
|
25
|
+
ECHO_SQL: bool = False
|
|
26
|
+
|
|
27
|
+
_instance = None
|
|
28
|
+
|
|
29
|
+
def __new__(cls):
|
|
30
|
+
if cls._instance is None:
|
|
31
|
+
cls._instance = super().__new__(cls)
|
|
32
|
+
cls._instance._initialized = False
|
|
33
|
+
return cls._instance
|
|
34
|
+
|
|
35
|
+
def __init__(
|
|
36
|
+
self,
|
|
37
|
+
username: Optional[str] = None,
|
|
38
|
+
password: Optional[str] = None,
|
|
39
|
+
host: Optional[str] = None,
|
|
40
|
+
port: Optional[str] = None,
|
|
41
|
+
database: Optional[str] = None,
|
|
42
|
+
):
|
|
43
|
+
if self._initialized:
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
# Use provided values or fall back to environment variables
|
|
47
|
+
USERNAME = username or os.getenv("POSTGRES_USER")
|
|
48
|
+
PASSWORD = password or os.getenv("POSTGRES_PASSWORD")
|
|
49
|
+
HOST = host or os.getenv("POSTGRES_HOST")
|
|
50
|
+
PORT = port or os.getenv("POSTGRES_PORT")
|
|
51
|
+
DATABASE = database or os.getenv("POSTGRES_DB")
|
|
52
|
+
self.DATABASE_URL = f"postgresql+asyncpg://{USERNAME}:{PASSWORD}@{HOST}:{PORT}/{DATABASE}"
|
|
53
|
+
|
|
54
|
+
# Create async engine
|
|
55
|
+
self.engine: AsyncEngine = create_async_engine(
|
|
56
|
+
self.DATABASE_URL,
|
|
57
|
+
pool_size=self.POOL_SIZE,
|
|
58
|
+
max_overflow=self.MAX_OVERFLOW,
|
|
59
|
+
pool_timeout=self.POOL_TIMEOUT,
|
|
60
|
+
pool_recycle=self.POOL_RECYCLE,
|
|
61
|
+
echo=self.ECHO_SQL,
|
|
62
|
+
pool_pre_ping=True,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
# Create async session factory
|
|
66
|
+
self.async_session_factory = async_sessionmaker(
|
|
67
|
+
self.engine,
|
|
68
|
+
class_=AsyncSession,
|
|
69
|
+
expire_on_commit=False,
|
|
70
|
+
autoflush=False,
|
|
71
|
+
autocommit=False,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
self._initialized = True
|
|
75
|
+
logger.info("Database manager initialized")
|
|
76
|
+
|
|
77
|
+
async def init_db(self, drop_existing: bool = False):
|
|
78
|
+
"""
|
|
79
|
+
Initialize database tables.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
drop_existing: If True, drops all existing tables before creating
|
|
83
|
+
"""
|
|
84
|
+
try:
|
|
85
|
+
async with self.engine.begin() as conn:
|
|
86
|
+
if drop_existing:
|
|
87
|
+
logger.warning("Dropping all existing tables")
|
|
88
|
+
await conn.run_sync(BaseModel.metadata.drop_all)
|
|
89
|
+
|
|
90
|
+
await conn.run_sync(BaseModel.metadata.create_all)
|
|
91
|
+
|
|
92
|
+
logger.info("Database tables initialized successfully")
|
|
93
|
+
|
|
94
|
+
except Exception as e:
|
|
95
|
+
logger.error(f"Failed to initialize database: {e}")
|
|
96
|
+
raise
|
|
97
|
+
|
|
98
|
+
@asynccontextmanager
|
|
99
|
+
async def get_session(self) -> AsyncGenerator[AsyncSession, None]:
|
|
100
|
+
"""
|
|
101
|
+
Async context manager for database sessions.
|
|
102
|
+
|
|
103
|
+
Usage:
|
|
104
|
+
async with db_manager.get_session() as session:
|
|
105
|
+
user = await session.get(User, user_id)
|
|
106
|
+
await session.commit()
|
|
107
|
+
"""
|
|
108
|
+
session = self.async_session_factory()
|
|
109
|
+
try:
|
|
110
|
+
yield session
|
|
111
|
+
await session.commit()
|
|
112
|
+
except Exception as e:
|
|
113
|
+
await session.rollback()
|
|
114
|
+
logger.error(f"Session error: {e}")
|
|
115
|
+
raise
|
|
116
|
+
finally:
|
|
117
|
+
await session.close()
|
|
118
|
+
|
|
119
|
+
async def get_db(self) -> AsyncGenerator[AsyncSession, None]:
|
|
120
|
+
"""
|
|
121
|
+
Dependency injection for FastAPI/similar frameworks.
|
|
122
|
+
|
|
123
|
+
Usage:
|
|
124
|
+
@app.get("/users")
|
|
125
|
+
async def get_users(db: AsyncSession = Depends(db_manager.get_db)):
|
|
126
|
+
result = await db.execute(select(User))
|
|
127
|
+
return result.scalars().all()
|
|
128
|
+
"""
|
|
129
|
+
session = self.async_session_factory()
|
|
130
|
+
try:
|
|
131
|
+
yield session
|
|
132
|
+
finally:
|
|
133
|
+
await session.close()
|
|
134
|
+
|
|
135
|
+
async def close(self):
|
|
136
|
+
"""Dispose of the engine and close all connections."""
|
|
137
|
+
await self.engine.dispose()
|
|
138
|
+
logger.info("Database connections closed")
|
|
139
|
+
|
|
140
|
+
async def health_check(self) -> bool:
|
|
141
|
+
"""Check if database connection is healthy."""
|
|
142
|
+
try:
|
|
143
|
+
async with self.get_session() as session:
|
|
144
|
+
await session.execute(text("SELECT 1"))
|
|
145
|
+
return True
|
|
146
|
+
except Exception as e:
|
|
147
|
+
logger.error(f"Health check failed: {e}")
|
|
148
|
+
return False
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from sqlalchemy.ext.declarative import declarative_base
|
|
2
|
+
from sqlalchemy import Column
|
|
3
|
+
from sqlalchemy.dialects.postgresql import UUID
|
|
4
|
+
import uuid
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
|
|
7
|
+
Base = declarative_base()
|
|
8
|
+
|
|
9
|
+
class TimestampMixin:
|
|
10
|
+
"""Mixin for models that need timestamp functionality."""
|
|
11
|
+
|
|
12
|
+
@staticmethod
|
|
13
|
+
def get_timestamp():
|
|
14
|
+
return datetime.utcnow().isoformat()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class BaseModel(Base):
|
|
18
|
+
"""Abstract base model with common functionality."""
|
|
19
|
+
__abstract__ = True
|
|
20
|
+
|
|
21
|
+
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
|
22
|
+
|
|
23
|
+
def to_dict(self):
|
|
24
|
+
"""Convert model to dictionary."""
|
|
25
|
+
return {c.name: getattr(self, c.name) for c in self.__table__.columns}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Job and JobLog models for persistent storage of job execution data.
|
|
3
|
+
"""
|
|
4
|
+
from sqlalchemy import Column, String, Text, DateTime, Integer, ForeignKey, Index
|
|
5
|
+
from sqlalchemy.dialects.postgresql import JSONB
|
|
6
|
+
from sqlalchemy.orm import relationship
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
import enum
|
|
9
|
+
|
|
10
|
+
from wizelit_sdk.models.base import BaseModel
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class JobStatus(str, enum.Enum):
|
|
15
|
+
"""Enumeration of possible job statuses."""
|
|
16
|
+
RUNNING = "running"
|
|
17
|
+
COMPLETED = "completed"
|
|
18
|
+
FAILED = "failed"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class JobModel(BaseModel):
|
|
22
|
+
"""
|
|
23
|
+
Persistent storage for job execution data.
|
|
24
|
+
Tracks status, results, and errors for long-running jobs.
|
|
25
|
+
"""
|
|
26
|
+
__tablename__ = "jobs"
|
|
27
|
+
|
|
28
|
+
id = Column(String(64), primary_key=True) # JOB-xxxxx
|
|
29
|
+
status = Column(String(20), default=JobStatus.RUNNING.value, nullable=False)
|
|
30
|
+
result = Column(JSONB, nullable=True) # JSON result for completed jobs
|
|
31
|
+
error = Column(Text, nullable=True) # Error message for failed jobs
|
|
32
|
+
created_at = Column(DateTime, default=datetime.utcnow, nullable=False)
|
|
33
|
+
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow, nullable=False)
|
|
34
|
+
|
|
35
|
+
# Relationship to logs
|
|
36
|
+
logs = relationship("JobLogModel", back_populates="job", cascade="all, delete-orphan")
|
|
37
|
+
|
|
38
|
+
# Index for faster queries
|
|
39
|
+
__table_args__ = (
|
|
40
|
+
Index('idx_job_status', 'status'),
|
|
41
|
+
Index('idx_job_created_at', 'created_at'),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
def __repr__(self):
|
|
45
|
+
return f"<JobModel(id={self.id}, status={self.status})>"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class JobLogModel(BaseModel):
|
|
49
|
+
"""
|
|
50
|
+
Persistent storage for individual job log entries.
|
|
51
|
+
Captures timestamped log messages with severity levels.
|
|
52
|
+
"""
|
|
53
|
+
__tablename__ = "job_logs"
|
|
54
|
+
|
|
55
|
+
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
56
|
+
job_id = Column(String(64), ForeignKey("jobs.id", ondelete="CASCADE"), nullable=False)
|
|
57
|
+
message = Column(Text, nullable=False)
|
|
58
|
+
level = Column(String(20), nullable=False) # INFO, ERROR, WARNING, DEBUG
|
|
59
|
+
timestamp = Column(DateTime, default=datetime.utcnow, nullable=False)
|
|
60
|
+
|
|
61
|
+
# Relationship to job
|
|
62
|
+
job = relationship("JobModel", back_populates="logs")
|
|
63
|
+
|
|
64
|
+
# Indexes for faster queries
|
|
65
|
+
__table_args__ = (
|
|
66
|
+
Index('idx_job_log_job_id', 'job_id'),
|
|
67
|
+
Index('idx_job_log_timestamp', 'timestamp'),
|
|
68
|
+
Index('idx_job_log_job_id_timestamp', 'job_id', 'timestamp'),
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
def __repr__(self):
|
|
72
|
+
return f"<JobLogModel(id={self.id}, job_id={self.job_id}, level={self.level})>"
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: wizelit-sdk
|
|
3
|
+
Version: 0.1.23
|
|
4
|
+
Summary: Wizelit Agent Wrapper - Internal utility package
|
|
5
|
+
Author-email: Your Name <your.email@company.com>
|
|
6
|
+
Requires-Python: >=3.10
|
|
7
|
+
Requires-Dist: asyncpg>=0.26.0
|
|
8
|
+
Requires-Dist: fastmcp>=0.1.0
|
|
9
|
+
Requires-Dist: redis>=4.5.0
|
|
10
|
+
Requires-Dist: sqlalchemy>=1.4
|
|
11
|
+
Requires-Dist: typing-extensions>=4.0.0
|
|
12
|
+
Provides-Extra: dev
|
|
13
|
+
Requires-Dist: black>=22.0.0; extra == 'dev'
|
|
14
|
+
Requires-Dist: build>=1.0.0; extra == 'dev'
|
|
15
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
16
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
17
|
+
Provides-Extra: streaming
|
|
18
|
+
Requires-Dist: redis>=4.5.0; extra == 'streaming'
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# wizelit-sdk
|
|
22
|
+
|
|
23
|
+
Internal utility package for Wizelit Agent operations.
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
### Install from Git Repository
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Install latest version from main branch
|
|
31
|
+
uv pip install git+https://github.com/your-org/wizelit-sdk.git
|
|
32
|
+
|
|
33
|
+
# Install specific version
|
|
34
|
+
uv pip install git+https://github.com/your-org/wizelit-sdk.git@v0.1.0
|
|
35
|
+
|
|
36
|
+
# Install with SSH (Recommended for Private Repos)
|
|
37
|
+
uv pip install git+ssh://git@github.com/your-org/wizelit-sdk.git@v0.1.0
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Add to pyproject.toml
|
|
41
|
+
|
|
42
|
+
```toml
|
|
43
|
+
[project]
|
|
44
|
+
dependencies = [
|
|
45
|
+
"wizelit-sdk @ git+ssh://git@github.com/your-org/wizelit-sdk.git@v0.1.0"
|
|
46
|
+
]
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Usage
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from wizelit_agent_wrapper import your_module
|
|
53
|
+
|
|
54
|
+
# Use the wrapper
|
|
55
|
+
result = your_module.function()
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Development
|
|
59
|
+
|
|
60
|
+
### Setup Development Environment
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# Clone repository
|
|
64
|
+
git clone https://github.com/your-org/wizelit-sdk.git
|
|
65
|
+
cd wizelit-sdk
|
|
66
|
+
|
|
67
|
+
# Set up environment
|
|
68
|
+
make setup
|
|
69
|
+
|
|
70
|
+
# Activate virtual environment
|
|
71
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
72
|
+
|
|
73
|
+
# Install in development mode
|
|
74
|
+
make install-dev
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Available Make Commands
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
make setup # Set up development environment
|
|
81
|
+
make install-dev # Install in development mode
|
|
82
|
+
make test # Run tests
|
|
83
|
+
make format # Format code with black
|
|
84
|
+
make lint # Lint code with ruff
|
|
85
|
+
make check # Run tests and linting
|
|
86
|
+
make clean # Clean build artifacts
|
|
87
|
+
make build # Build package
|
|
88
|
+
make release # Create new release (interactive)
|
|
89
|
+
make tag VERSION=x.x.x # Create specific version tag
|
|
90
|
+
make push # Push code and tags
|
|
91
|
+
make version # Show current version
|
|
92
|
+
make versions # List all available versions
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Contributing
|
|
96
|
+
|
|
97
|
+
1. Create a feature branch
|
|
98
|
+
2. Make your changes
|
|
99
|
+
3. Run tests and linting: `make check`
|
|
100
|
+
4. Commit your changes
|
|
101
|
+
5. Push and create a pull request
|
|
102
|
+
|
|
103
|
+
## Versioning
|
|
104
|
+
|
|
105
|
+
We use [Semantic Versioning](https://semver.org/):
|
|
106
|
+
- **MAJOR** version for incompatible API changes
|
|
107
|
+
- **MINOR** version for new functionality (backward compatible)
|
|
108
|
+
- **PATCH** version for bug fixes
|
|
109
|
+
|
|
110
|
+
See [CHANGELOG.md](CHANGELOG.md) for version history.
|
|
111
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
wizelit_sdk/__init__.py,sha256=6UyHRL8Wu0CAWHIErlmDZFxqI_BZwsco_DWnoMr_H6A,485
|
|
2
|
+
wizelit_sdk/database.py,sha256=39Vu5A8CV7u7ItIwRgrkAugD9UqNbG5vQrCsMOgTamc,4631
|
|
3
|
+
wizelit_sdk/agent_wrapper/__init__.py,sha256=q4xsoiAyI_KM9TSL9rFfxPzGrONJt3GF6MlEx6vDkis,321
|
|
4
|
+
wizelit_sdk/agent_wrapper/agent_wrapper.py,sha256=r1N47KT4Sh6Gudt6ZEFNXvDDD8g4kOkuwTm19K9Np9c,22644
|
|
5
|
+
wizelit_sdk/agent_wrapper/job.py,sha256=Vvmm6eTX1cbXpFuwPMLaom6hEgdk3ovitnaj9d9p02o,13804
|
|
6
|
+
wizelit_sdk/agent_wrapper/streaming.py,sha256=f0VV3IzGAlfQY_cw2OHgxWjvM16Hs42_b700EUX2QpY,6633
|
|
7
|
+
wizelit_sdk/agent_wrapper/utils.py,sha256=OUihj3Kk6uI0y7bDr_L_YQs33vSK0GAJRl0c4km4DNs,951
|
|
8
|
+
wizelit_sdk/models/__init__.py,sha256=UB7nfHoE6hGcjfzy7w8AmuKVmYrTg9wIgGjG8GWziJ0,143
|
|
9
|
+
wizelit_sdk/models/base.py,sha256=aEPGMZVczKnlWz4Ps99e_xI5TcuSV3DxCigRMU5BnhE,704
|
|
10
|
+
wizelit_sdk/models/job.py,sha256=P68Vb1k77Z_Tha4dE0GzrLPa0LJrnMCxyYMENZyD7Kc,2480
|
|
11
|
+
wizelit_sdk-0.1.23.dist-info/METADATA,sha256=ta0J_LwnCa4NGBO2q-v0c23_G-b-SfK6Okhhzr-3zjM,2778
|
|
12
|
+
wizelit_sdk-0.1.23.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
13
|
+
wizelit_sdk-0.1.23.dist-info/RECORD,,
|