itzam 1.0.0__tar.gz

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.
itzam-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,131 @@
1
+ Metadata-Version: 2.4
2
+ Name: itzam
3
+ Version: 1.0.0
4
+ Summary: Python SDK to interact with Itzam's API
5
+ Author-email: Joaquim Cassano <joaquim@cassano.com.br>
6
+ Requires-Python: <4.0,>=3.10
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: pydantic
9
+ Requires-Dist: requests
10
+ Requires-Dist: rich
11
+
12
+ # Itzam python sdk
13
+ ![itzam logo](https://pbs.twimg.com/profile_banners/1930643525021937664/1749136962/600x200)
14
+
15
+ ## Overview
16
+
17
+ Itzam Python SDK provides a simple interface to interact with the [Itzam API](https://itz.am) for text generation, thread management, model listing, and run inspection.
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ pip install itzam
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ```python
28
+ from itzam import Itzam
29
+
30
+ client = Itzam("your-api-key")
31
+ response = client.text.generate(
32
+ workflow_slug="your_workflow_slug",
33
+ input="Hello, Itzam!",
34
+ stream=False
35
+ )
36
+ print(response.text)
37
+ ```
38
+
39
+ ## Authentication
40
+
41
+ You can provide your API key directly or set it as an environment variable:
42
+
43
+ ```bash
44
+ export ITZAM_API_KEY=your-api-key
45
+ ```
46
+
47
+ Then initialize without arguments:
48
+
49
+ ```python
50
+ from itzam import Itzam
51
+ client = Itzam()
52
+ ```
53
+
54
+ ## Features
55
+
56
+ - **Text Generation**: Generate text using your workflows.
57
+ - **Threads**: Create and manage threads for conversations.
58
+ - **Models**: List available models and their details.
59
+ - **Runs**: Inspect previous runs and their metadata.
60
+
61
+ ## Usage Examples
62
+
63
+ ### Generate Text
64
+
65
+ ```python
66
+ response = client.text.generate(
67
+ workflow_slug="your_workflow_slug",
68
+ input="Write a poem about the sea."
69
+ )
70
+ print(response.text)
71
+ ```
72
+
73
+ ### Stream Text Generation
74
+
75
+ ```python
76
+ for delta in client.text.generate(
77
+ workflow_slug="your_workflow_slug",
78
+ input="Tell me a story.",
79
+ stream=True
80
+ ):
81
+ print(delta, end="", flush=True)
82
+ ```
83
+
84
+ ### List Models
85
+
86
+ ```python
87
+ models = client.models.list()
88
+ for model in models:
89
+ print(model.name, model.tag)
90
+ ```
91
+
92
+ ### Create a Thread
93
+
94
+ ```python
95
+ thread = client.threads.create(
96
+ workflow_slug="your_workflow_slug",
97
+ name="Support Conversation"
98
+ )
99
+ print(thread.id)
100
+ ```
101
+
102
+ ### Get a Run
103
+
104
+ ```python
105
+ run = client.runs.get("run_id")
106
+ print(run.output)
107
+ ```
108
+
109
+ ## Advanced
110
+
111
+ You can specify a custom API base URL if needed:
112
+
113
+ ```python
114
+ client = Itzam(api_key="your-api-key", base_url="https://itz.am")
115
+ ```
116
+
117
+ ## Requirements
118
+
119
+ - Python 3.10+
120
+ - `requests`
121
+ - `pydantic`
122
+ - `rich`
123
+ - `python-dotenv` (optional, for environment variable loading)
124
+
125
+ ## License
126
+
127
+ MIT
128
+
129
+ ---
130
+
131
+ For more details, see the [API documentation](https://itz.am).
itzam-1.0.0/README.md ADDED
@@ -0,0 +1,120 @@
1
+ # Itzam python sdk
2
+ ![itzam logo](https://pbs.twimg.com/profile_banners/1930643525021937664/1749136962/600x200)
3
+
4
+ ## Overview
5
+
6
+ Itzam Python SDK provides a simple interface to interact with the [Itzam API](https://itz.am) for text generation, thread management, model listing, and run inspection.
7
+
8
+ ## Installation
9
+
10
+ ```bash
11
+ pip install itzam
12
+ ```
13
+
14
+ ## Quick Start
15
+
16
+ ```python
17
+ from itzam import Itzam
18
+
19
+ client = Itzam("your-api-key")
20
+ response = client.text.generate(
21
+ workflow_slug="your_workflow_slug",
22
+ input="Hello, Itzam!",
23
+ stream=False
24
+ )
25
+ print(response.text)
26
+ ```
27
+
28
+ ## Authentication
29
+
30
+ You can provide your API key directly or set it as an environment variable:
31
+
32
+ ```bash
33
+ export ITZAM_API_KEY=your-api-key
34
+ ```
35
+
36
+ Then initialize without arguments:
37
+
38
+ ```python
39
+ from itzam import Itzam
40
+ client = Itzam()
41
+ ```
42
+
43
+ ## Features
44
+
45
+ - **Text Generation**: Generate text using your workflows.
46
+ - **Threads**: Create and manage threads for conversations.
47
+ - **Models**: List available models and their details.
48
+ - **Runs**: Inspect previous runs and their metadata.
49
+
50
+ ## Usage Examples
51
+
52
+ ### Generate Text
53
+
54
+ ```python
55
+ response = client.text.generate(
56
+ workflow_slug="your_workflow_slug",
57
+ input="Write a poem about the sea."
58
+ )
59
+ print(response.text)
60
+ ```
61
+
62
+ ### Stream Text Generation
63
+
64
+ ```python
65
+ for delta in client.text.generate(
66
+ workflow_slug="your_workflow_slug",
67
+ input="Tell me a story.",
68
+ stream=True
69
+ ):
70
+ print(delta, end="", flush=True)
71
+ ```
72
+
73
+ ### List Models
74
+
75
+ ```python
76
+ models = client.models.list()
77
+ for model in models:
78
+ print(model.name, model.tag)
79
+ ```
80
+
81
+ ### Create a Thread
82
+
83
+ ```python
84
+ thread = client.threads.create(
85
+ workflow_slug="your_workflow_slug",
86
+ name="Support Conversation"
87
+ )
88
+ print(thread.id)
89
+ ```
90
+
91
+ ### Get a Run
92
+
93
+ ```python
94
+ run = client.runs.get("run_id")
95
+ print(run.output)
96
+ ```
97
+
98
+ ## Advanced
99
+
100
+ You can specify a custom API base URL if needed:
101
+
102
+ ```python
103
+ client = Itzam(api_key="your-api-key", base_url="https://itz.am")
104
+ ```
105
+
106
+ ## Requirements
107
+
108
+ - Python 3.10+
109
+ - `requests`
110
+ - `pydantic`
111
+ - `rich`
112
+ - `python-dotenv` (optional, for environment variable loading)
113
+
114
+ ## License
115
+
116
+ MIT
117
+
118
+ ---
119
+
120
+ For more details, see the [API documentation](https://itz.am).
@@ -0,0 +1,40 @@
1
+ """
2
+ The Python sdk for the Itzam API.
3
+ Example usage:
4
+
5
+ ```python
6
+ from itzam import Itzam
7
+
8
+ client = Itzam("your api key")
9
+ response = client.text.generate(
10
+ workflow_slug="your_workflow_slug",
11
+ input="your input text",
12
+ stream=False
13
+ )
14
+ print(response.text)
15
+ ```
16
+ """
17
+ from .text.client import TextClient
18
+ from .threads import ThreadsClient
19
+ from .runs import RunsClient
20
+ from .models import ModelsClient
21
+ import dotenv, os
22
+
23
+ class Itzam:
24
+ def __init__(self, api_key: str|None = None, base_url: str="https://itz.am"):
25
+ """
26
+ Initialize the Itzam SDK with the base URL and API key. If no API key is provided, it will look for the `ITZAM_API_KEY` environment variable.
27
+
28
+ :param base_url: The base URL for the Itzam API.
29
+ :param api_key: The API key for authentication.
30
+ """
31
+ if not api_key:
32
+ dotenv.load_dotenv()
33
+ api_key = os.getenv("ITZAM_API_KEY")
34
+ if not api_key:
35
+ raise ValueError("API key is required. Please provide it as an argument or set the ITZAM_API_KEY environment variable.")
36
+
37
+ self.text = TextClient(base_url=base_url, api_key=api_key)
38
+ self.threads = ThreadsClient(base_url=base_url, api_key=api_key)
39
+ self.runs = RunsClient(base_url=base_url, api_key=api_key)
40
+ self.models = ModelsClient(base_url=base_url, api_key=api_key)
@@ -0,0 +1,3 @@
1
+ """
2
+ Module responsible for managing all objects that serve as a foundation for other objects.
3
+ """
@@ -0,0 +1,34 @@
1
+ import requests
2
+ from ..utils.exceptions import raise_for_status
3
+ import logging
4
+ logger = logging.getLogger(__name__)
5
+ class BaseClient:
6
+ """ Base client for making HTTP requests to the Itzam API.
7
+ This class should be extended by specific API clients.
8
+ It handles the common functionality of making requests, including setting headers and handling errors.
9
+ """
10
+ def __init__(self, api_key: str, base_url: str):
11
+ self.base_url = base_url
12
+ self.api_key = api_key
13
+
14
+ def request(self, method: str, endpoint: str, params: dict = None, data: dict = None, **kwargs):
15
+ url = f"{self.base_url}{endpoint}"
16
+ headers = {
17
+ "Content-Type": "application/json",
18
+ "User-Agent": "Itzam-Python-SDK/1.0",
19
+ "Api-Key": self.api_key
20
+ }
21
+
22
+ response = requests.request(method, url, headers=headers, params=params, json=data, **kwargs)
23
+
24
+ logger.debug(f"Request URL: {response.request.url}")
25
+ logger.debug(f"Request Method: {response.request.method}")
26
+ logger.debug(f"Request Headers: {response.request.headers}")
27
+ logger.debug(f"Request Body: {response.request.body}")
28
+ logger.debug(f"Response Status Code: {response.status_code}")
29
+ logger.debug(f"Response body: {response.text}")
30
+
31
+ if response.status_code != 200:
32
+ raise_for_status(response)
33
+
34
+ return response
@@ -0,0 +1,6 @@
1
+ from .client import ModelsClient
2
+
3
+ __all__ = [
4
+ "ModelsClient",
5
+ ]
6
+
@@ -0,0 +1,28 @@
1
+ from .client import ModelsClient
2
+
3
+ if __name__ == "__main__":
4
+ from rich.console import Console
5
+ from rich.table import Table
6
+ import dotenv, os
7
+ dotenv.load_dotenv()
8
+ client = ModelsClient(base_url="https://itz.am", api_key=os.getenv("ITZAM_API_KEY"))
9
+ models = client.list()
10
+ console = Console()
11
+
12
+ table = Table(title="Models List")
13
+ table.add_column("Name", style="cyan", no_wrap=True)
14
+ table.add_column("Tag", style="magenta")
15
+ table.add_column("Deprecated", style="red")
16
+ table.add_column("Open Source", style="green")
17
+ table.add_column("Context Window Size", style="yellow")
18
+
19
+ for model in models:
20
+ table.add_row(
21
+ model.name,
22
+ model.tag,
23
+ "Yes" if model.deprecated else "No",
24
+ "Yes" if model.isOpenSource else "No",
25
+ str(model.contextWindowSize)
26
+ )
27
+
28
+ console.print(table)
@@ -0,0 +1,18 @@
1
+ from .models import Model
2
+ from ..base.client import BaseClient
3
+
4
+ class ModelsClient(BaseClient):
5
+ """
6
+ Client for interacting with the Itzam Models API.
7
+ """
8
+
9
+ def __init__(self, base_url: str, api_key: str):
10
+ super().__init__(base_url=base_url, api_key=api_key)
11
+
12
+ def list(self) -> list[Model]:
13
+ """
14
+ List all available models.
15
+ """
16
+ endpoint = "/api/v1/models"
17
+ response = self.request(method="GET", endpoint=endpoint)
18
+ return [Model.model_validate(model) for model in response.json().get("models", [])]
@@ -0,0 +1,16 @@
1
+ from pydantic import BaseModel, Field
2
+
3
+ class Provider(BaseModel):
4
+ name: str
5
+
6
+ class Model(BaseModel):
7
+ name: str
8
+ tag: str
9
+ deprecated: bool
10
+ hasVision: bool
11
+ hasReasoningCapability: bool
12
+ isOpenSource: bool
13
+ contextWindowSize: int
14
+ inputPerMillionTokenCost: float
15
+ outputPerMillionTokenCost: float
16
+ provider: Provider
@@ -0,0 +1,5 @@
1
+ from .client import RunsClient
2
+
3
+ __all__ = [
4
+ "RunsClient",
5
+ ]
@@ -0,0 +1,18 @@
1
+ from .models import Run
2
+ from ..base.client import BaseClient
3
+
4
+ class RunsClient(BaseClient):
5
+ """
6
+ Client for interacting with the Itzam Runs API.
7
+ """
8
+
9
+ def __init__(self, base_url: str, api_key: str):
10
+ super().__init__(base_url=base_url, api_key=api_key)
11
+
12
+ def get(self, run_id: str) -> Run:
13
+ """
14
+ Get a run by its ID.
15
+ """
16
+ endpoint = f"/api/v1/runs/{run_id}"
17
+ response = self.request(method="GET", endpoint=endpoint)
18
+ return Run.model_validate(response.json())
@@ -0,0 +1,34 @@
1
+ from pydantic import BaseModel, Field
2
+ from typing import List, Optional
3
+ from datetime import datetime
4
+ from ..text.models import ModelInput as Model, Attachment
5
+
6
+
7
+ class AttachmentResponse(Attachment):
8
+ id: str
9
+
10
+
11
+ class Knowledge(BaseModel):
12
+ id: str
13
+ title: str
14
+ url: str
15
+ type: str
16
+
17
+
18
+ class Run(BaseModel):
19
+ id: str
20
+ origin: str
21
+ status: str
22
+ input: str
23
+ output: str
24
+ prompt: str
25
+ input_tokens: int = Field(alias="inputTokens")
26
+ output_tokens: int = Field(alias="outputTokens")
27
+ cost: str
28
+ duration_in_ms: int = Field(alias="durationInMs")
29
+ thread_id: str|None = Field(alias="threadId", default=None)
30
+ model: Model
31
+ attachments: List[Attachment] = Field(default=[])
32
+ knowledge: List[Knowledge ] = Field(default=[])
33
+ workflow_id: str = Field(alias="workflowId")
34
+ created_at: datetime = Field(alias="createdAt")
@@ -0,0 +1,14 @@
1
+ from .client import TextClient
2
+ from .models import (
3
+ Attachment,
4
+ ModelInput,
5
+ ResponseMetadata,
6
+ Response
7
+ )
8
+
9
+ __all__ = ["TextClient",
10
+ "Attachment",
11
+ "ModelInput",
12
+ "ResponseMetadata",
13
+ "Response"
14
+ ]
@@ -0,0 +1,60 @@
1
+ import requests
2
+ from .models import (
3
+ Attachment,
4
+ ModelInput,
5
+ ResponseMetadata,
6
+ Response
7
+ )
8
+ from ..base.client import BaseClient
9
+ import json
10
+
11
+ class TextClient(BaseClient):
12
+ """
13
+ Client for interacting with the Itzam Text API.
14
+ """
15
+
16
+ def __init__(self, base_url: str, api_key: str):
17
+ super().__init__(base_url=base_url, api_key=api_key)
18
+
19
+ def generate(
20
+ self,
21
+ input: str,
22
+ workflow_slug: str | None = None,
23
+ thread_id: str | None = None,
24
+ attachments: list[Attachment] = None,
25
+ stream: bool = False
26
+ ):
27
+ """
28
+ Generate text using the specified model and prompt.
29
+ If stream=True, returns a generator yielding text deltas.
30
+ """
31
+ if not workflow_slug and not thread_id:
32
+ raise ValueError("Either 'thread_id' or 'worflow_slug' must be provided.")
33
+ endpoint = "/api/v1/generate/text"
34
+ data = {
35
+ "input": input,
36
+ "workflowSlug": workflow_slug,
37
+ **({"threadId": thread_id} if thread_id else {}),
38
+ **({"attachments": [attachment.model_dump() for attachment in attachments]} if attachments else {})
39
+ }
40
+
41
+ if stream == True:
42
+ return self._stream_text("/api/v1/stream/text", data)
43
+ else:
44
+ response = self.request(method="POST", endpoint=endpoint, data=data)
45
+ return Response.model_validate(response.json())
46
+
47
+ def _stream_text(self, endpoint, data):
48
+ """
49
+ Internal method to handle streaming text responses.
50
+ Yields text deltas as they arrive.
51
+ """
52
+ response = self.request(method="POST", endpoint=endpoint, data=data, stream=True)
53
+ for line in response.iter_lines():
54
+ if line:
55
+ try:
56
+ event = json.loads(line.decode("utf-8").removeprefix("data: "))
57
+ if event.get("type") == "text-delta":
58
+ yield event.get("textDelta")
59
+ except Exception as e:
60
+ continue
@@ -0,0 +1,39 @@
1
+ from pydantic import BaseModel, Field
2
+
3
+ class Attachment(BaseModel):
4
+ """
5
+ Represents an attachment in a text message.
6
+ """
7
+ file: str = Field(
8
+ ...,
9
+ description="The file path or URL of the attachment. Either a base64 encoded string or a URL."
10
+ )
11
+ mimetype: str = Field(
12
+ ...,
13
+ description="The MIME type of the attachment, e.g., 'image/png', 'application/pdf'."
14
+ )
15
+
16
+ class ModelInput(BaseModel):
17
+ """
18
+ Represents a model used for text generation.
19
+ """
20
+ name: str = Field(..., description="The name of the model used for this generation.")
21
+ tag: str = Field(..., description="The tag of the model used for text generation.")
22
+
23
+ class ResponseMetadata(BaseModel):
24
+ """
25
+ Metadata for the response.
26
+ """
27
+ run_id: str = Field(alias="runId", description="The ID of the run created for this generation")
28
+ cost: str = Field(description="The cost of the generation in USD")
29
+ model: ModelInput
30
+ duration: int = Field( description="The duration of the generation in milliseconds", alias="durationInMs")
31
+ input_tokens: int = Field(alias="inputTokens", description="The number of input tokens used in the generation")
32
+ output_tokens: int = Field(alias="outputTokens", description="The number of output tokens used in this generation")
33
+
34
+ class Response(BaseModel):
35
+ """
36
+ Represents the response from the text generation API.
37
+ """
38
+ text: str = Field(description="The generated text")
39
+ metadata: ResponseMetadata = Field(description="Metadata about the generation process")
@@ -0,0 +1,7 @@
1
+ from .client import ThreadsClient
2
+ from .models import Thread
3
+
4
+ __all__ = [
5
+ "ThreadsClient",
6
+ "Thread"
7
+ ]
@@ -0,0 +1,53 @@
1
+ from ..base.client import BaseClient
2
+ from .models import (
3
+ Thread
4
+ )
5
+
6
+ class ThreadsClient(BaseClient):
7
+ """
8
+ Client for interacting with the Itzam Threads API.
9
+ """
10
+
11
+ def __init__(self, base_url: str, api_key: str):
12
+ super().__init__(base_url=base_url, api_key=api_key)
13
+
14
+ def create(self, workflow_slug:str, name:str|None = None, lookup_keys:list[str] = [], context_slugs:list[str] = []) -> Thread:
15
+ """
16
+ Create a new thread.
17
+ """
18
+ endpoint = "/api/v1/threads"
19
+ data = {
20
+ "workflowSlug": workflow_slug,
21
+ "name": name,
22
+ }
23
+ if lookup_keys:
24
+ data["lookupKeys"] = lookup_keys
25
+ if context_slugs:
26
+ data["contextSlugs"] = context_slugs
27
+ response = self.request(method="POST", endpoint=endpoint, data=data)
28
+ return Thread.model_validate(response.json())
29
+
30
+ def get(self, thread_id: str) -> Thread:
31
+ """
32
+ Get a thread by its ID.
33
+ """
34
+ endpoint = f"/api/v1/threads/{thread_id}"
35
+ response = self.request(method="GET", endpoint=endpoint)
36
+ return Thread.model_validate(response.json())
37
+
38
+ def from_workflow(self, workflow_slug: str) -> list[Thread]:
39
+ """
40
+ Get all threads for a specific workflow.
41
+ """
42
+ endpoint = f"/api/v1/threads/workflow/{workflow_slug}"
43
+ response = self.request(method="GET", endpoint=endpoint)
44
+ return [Thread.model_validate(thread) for thread in response.json().get("threads", [])]
45
+
46
+ def runs_from_thread(self, thread_id: str) -> list[dict]:
47
+ """
48
+ Get all runs for a specific thread.
49
+ """
50
+ endpoint = f"/api/v1/threads/{thread_id}/runs"
51
+ response = self.request(method="GET", endpoint=endpoint)
52
+ return response.json().get("runs", [])
53
+
@@ -0,0 +1,11 @@
1
+ from pydantic import BaseModel, Field
2
+
3
+ class Thread(BaseModel):
4
+ name: str | None = Field(default=None, description="The name of the thread (optional, will auto-generate if not provided)")
5
+ lookup_keys: list[str] | None = Field(default=None, description="Optional lookup keys for finding the thread later", alias="lookupKeys")
6
+ context_slugs: list[str] | None = Field(default=None, description="Optional context slugs to append the context to the thread", alias="contextSlugs")
7
+ created_at: str | None = Field(default=None, description="The date and time when the thread was created", alias="createdAt")
8
+ updated_at: str | None = Field(default=None, description="The date and time when the thread was last updated", alias="updatedAt")
9
+ id: str = Field(..., description="The unique identifier for the thread")
10
+
11
+
@@ -0,0 +1,187 @@
1
+ from http import HTTPStatus
2
+ from typing import Literal
3
+
4
+ import requests
5
+
6
+
7
+ class APIError(Exception):
8
+ """The exception was raised due to an API error."""
9
+
10
+ message: str
11
+ request: str
12
+
13
+ def __init__(self, message: str, request: requests.Request) -> None:
14
+ super().__init__(message)
15
+ self.message = """The exception was raised due to an API error."""
16
+ self.request = request
17
+
18
+ def __str__(self) -> str:
19
+ if self.message:
20
+ return f'{self.message}'
21
+ return ''
22
+
23
+
24
+ class APIStatusError(APIError):
25
+ """Raised when an API response has a status code of 4xx or 5xx."""
26
+
27
+ response: requests.Response
28
+ status_code: int
29
+
30
+ def __init__(
31
+ self, message: str = '', *, response: requests.Response
32
+ ) -> None:
33
+ super().__init__(message, response.request)
34
+ self.response = response
35
+ self.status_code = response.status_code
36
+
37
+
38
+ class ForbiddenRequest(APIStatusError):
39
+ """
40
+ Means that the request was unsuccessful due to a forbidden request.
41
+ Maybe your API key is wrong?
42
+ """
43
+
44
+ status_code: Literal[HTTPStatus.FORBIDDEN]
45
+
46
+ def __init__(self, response: requests.Response, message: str = ''):
47
+ super().__init__(message, response=response)
48
+ self.message = (
49
+ 'Means that the request was unsuccessful due to a '
50
+ 'forbidden request. Maybe your API key is wrong?'
51
+ )
52
+ self.response = response
53
+ self.status_code = HTTPStatus.FORBIDDEN
54
+
55
+ def __str__(self) -> str:
56
+ if self.message:
57
+ return (
58
+ f'{self.message} \n Status Code: {self.response.status_code}'
59
+ f' | Response: {self.response.text}'
60
+ )
61
+ return str(self.response.content, 'utf-8')
62
+
63
+
64
+ class UnauthorizedRequest(APIStatusError):
65
+ """
66
+ Means that the request was unsuccessful due to a forbidden request.
67
+ Maybe your API key doesn't have enought permissions
68
+ """
69
+
70
+ status_code: Literal[HTTPStatus.UNAUTHORIZED]
71
+
72
+ def __init__(self, response: requests.Response, message: str = ''):
73
+ super().__init__(message, response=response)
74
+ self.message = (
75
+ 'Means that the request was unsuccessful due to a forbidden '
76
+ "request. Maybe your API key doesn't have enought permissions"
77
+ )
78
+ self.response = response
79
+ self.status_code = HTTPStatus.UNAUTHORIZED
80
+
81
+ def __str__(self) -> str:
82
+ if self.message:
83
+ return (
84
+ f'{self.message} | Status Code: {self.response.status_code}'
85
+ f' | Response: {self.response.text}'
86
+ )
87
+ return str(self.response.content, 'utf-8')
88
+
89
+
90
+ class APIConnectionError(APIError):
91
+ """The request was unsuccessful due to a connection error.
92
+ Check your internet connection"""
93
+
94
+ def __init__(
95
+ self,
96
+ *,
97
+ message: str = (
98
+ 'The request was unsuccessful due to a connection error.'
99
+ ' Check your internet connection'
100
+ ),
101
+ request: requests.Request,
102
+ ) -> None:
103
+ super().__init__(message, request)
104
+
105
+
106
+ class APITimeoutError(APIConnectionError):
107
+ """The request got timed out. You might try checking
108
+ your internet connection."""
109
+
110
+ def __init__(self, request: requests.Request) -> None:
111
+ super().__init__(
112
+ message='Request timed out. Check your internet connection',
113
+ request=request,
114
+ )
115
+
116
+
117
+ class BadRequestError(APIStatusError):
118
+ """The request was unsuccessful due to a bad request.
119
+ Maybe the request syntax is wrong"""
120
+
121
+ status_code: Literal[HTTPStatus.BAD_REQUEST]
122
+
123
+ def __init__(self, response: requests.Response) -> None:
124
+ self.response = response
125
+ self.status_code = HTTPStatus.BAD_REQUEST
126
+
127
+ def __str__(self) -> str:
128
+ return (
129
+ f'The request was unsuccessful due to a bad request. '
130
+ 'Maybe the request syntax is wrong. Message error: '
131
+ f'{self.response.json()}'
132
+ )
133
+
134
+
135
+ class NotFoundError(APIStatusError):
136
+ status_code: Literal[HTTPStatus.NOT_FOUND]
137
+
138
+ def __init__(
139
+ self, message: str = '', *, response: requests.Response
140
+ ) -> None:
141
+ super().__init__(message, response=response)
142
+ self.status_code = HTTPStatus.NOT_FOUND
143
+
144
+ def __str__(self) -> str:
145
+ return (
146
+ 'The request was unsuccessful due to a not found error. '
147
+ f'Error status 404 | Requested URL: {self.response.url}'
148
+ )
149
+
150
+
151
+ class InternalServerError(APIStatusError):
152
+ """The request was unsuccessful due to an internal server error."""
153
+
154
+ status_code: Literal[500] = 500
155
+
156
+ def __init__(self, response: requests.Response) -> None:
157
+ super().__init__(
158
+ message=(
159
+ 'The request was unsuccessful due to an internal server error.'
160
+ " It's not your fault, just try again later."
161
+ ),
162
+ response=response,
163
+ )
164
+
165
+
166
+ def raise_for_status(response: requests.Response) -> None:
167
+ code_exc_dict = {
168
+ HTTPStatus.BAD_REQUEST: BadRequestError(response=response),
169
+ HTTPStatus.UNAUTHORIZED: UnauthorizedRequest(response=response),
170
+ HTTPStatus.FORBIDDEN: ForbiddenRequest(response=response),
171
+ HTTPStatus.NOT_FOUND: NotFoundError(response=response),
172
+ HTTPStatus.INTERNAL_SERVER_ERROR: InternalServerError(
173
+ response=response
174
+ ),
175
+ }
176
+
177
+ code = response.status_code
178
+ if code == HTTPStatus.OK:
179
+ return
180
+
181
+ if code not in code_exc_dict and code >= HTTPStatus.BAD_REQUEST:
182
+ raise APIStatusError(message=response.text, response=response)
183
+
184
+ raise code_exc_dict.get(
185
+ response.status_code,
186
+ APIError(message=response.text, request=response.request),
187
+ )
@@ -0,0 +1,131 @@
1
+ Metadata-Version: 2.4
2
+ Name: itzam
3
+ Version: 1.0.0
4
+ Summary: Python SDK to interact with Itzam's API
5
+ Author-email: Joaquim Cassano <joaquim@cassano.com.br>
6
+ Requires-Python: <4.0,>=3.10
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: pydantic
9
+ Requires-Dist: requests
10
+ Requires-Dist: rich
11
+
12
+ # Itzam python sdk
13
+ ![itzam logo](https://pbs.twimg.com/profile_banners/1930643525021937664/1749136962/600x200)
14
+
15
+ ## Overview
16
+
17
+ Itzam Python SDK provides a simple interface to interact with the [Itzam API](https://itz.am) for text generation, thread management, model listing, and run inspection.
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ pip install itzam
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ```python
28
+ from itzam import Itzam
29
+
30
+ client = Itzam("your-api-key")
31
+ response = client.text.generate(
32
+ workflow_slug="your_workflow_slug",
33
+ input="Hello, Itzam!",
34
+ stream=False
35
+ )
36
+ print(response.text)
37
+ ```
38
+
39
+ ## Authentication
40
+
41
+ You can provide your API key directly or set it as an environment variable:
42
+
43
+ ```bash
44
+ export ITZAM_API_KEY=your-api-key
45
+ ```
46
+
47
+ Then initialize without arguments:
48
+
49
+ ```python
50
+ from itzam import Itzam
51
+ client = Itzam()
52
+ ```
53
+
54
+ ## Features
55
+
56
+ - **Text Generation**: Generate text using your workflows.
57
+ - **Threads**: Create and manage threads for conversations.
58
+ - **Models**: List available models and their details.
59
+ - **Runs**: Inspect previous runs and their metadata.
60
+
61
+ ## Usage Examples
62
+
63
+ ### Generate Text
64
+
65
+ ```python
66
+ response = client.text.generate(
67
+ workflow_slug="your_workflow_slug",
68
+ input="Write a poem about the sea."
69
+ )
70
+ print(response.text)
71
+ ```
72
+
73
+ ### Stream Text Generation
74
+
75
+ ```python
76
+ for delta in client.text.generate(
77
+ workflow_slug="your_workflow_slug",
78
+ input="Tell me a story.",
79
+ stream=True
80
+ ):
81
+ print(delta, end="", flush=True)
82
+ ```
83
+
84
+ ### List Models
85
+
86
+ ```python
87
+ models = client.models.list()
88
+ for model in models:
89
+ print(model.name, model.tag)
90
+ ```
91
+
92
+ ### Create a Thread
93
+
94
+ ```python
95
+ thread = client.threads.create(
96
+ workflow_slug="your_workflow_slug",
97
+ name="Support Conversation"
98
+ )
99
+ print(thread.id)
100
+ ```
101
+
102
+ ### Get a Run
103
+
104
+ ```python
105
+ run = client.runs.get("run_id")
106
+ print(run.output)
107
+ ```
108
+
109
+ ## Advanced
110
+
111
+ You can specify a custom API base URL if needed:
112
+
113
+ ```python
114
+ client = Itzam(api_key="your-api-key", base_url="https://itz.am")
115
+ ```
116
+
117
+ ## Requirements
118
+
119
+ - Python 3.10+
120
+ - `requests`
121
+ - `pydantic`
122
+ - `rich`
123
+ - `python-dotenv` (optional, for environment variable loading)
124
+
125
+ ## License
126
+
127
+ MIT
128
+
129
+ ---
130
+
131
+ For more details, see the [API documentation](https://itz.am).
@@ -0,0 +1,24 @@
1
+ README.md
2
+ pyproject.toml
3
+ itzam/__init__.py
4
+ itzam.egg-info/PKG-INFO
5
+ itzam.egg-info/SOURCES.txt
6
+ itzam.egg-info/dependency_links.txt
7
+ itzam.egg-info/requires.txt
8
+ itzam.egg-info/top_level.txt
9
+ itzam/base/__init__.py
10
+ itzam/base/client.py
11
+ itzam/models/__init__.py
12
+ itzam/models/__main__.py
13
+ itzam/models/client.py
14
+ itzam/models/models.py
15
+ itzam/runs/__init__.py
16
+ itzam/runs/client.py
17
+ itzam/runs/models.py
18
+ itzam/text/__init__.py
19
+ itzam/text/client.py
20
+ itzam/text/models.py
21
+ itzam/threads/__init__.py
22
+ itzam/threads/client.py
23
+ itzam/threads/models.py
24
+ itzam/utils/exceptions.py
@@ -0,0 +1,3 @@
1
+ pydantic
2
+ requests
3
+ rich
@@ -0,0 +1 @@
1
+ itzam
@@ -0,0 +1,16 @@
1
+ [build-system]
2
+ requires = ["setuptools >= 77.0.3"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "itzam"
7
+ version = "1.0.0"
8
+ description = "Python SDK to interact with Itzam's API"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10,<4.0"
11
+ authors = [{ name = "Joaquim Cassano", email = "joaquim@cassano.com.br" }]
12
+ dependencies = [
13
+ "pydantic",
14
+ "requests",
15
+ "rich"
16
+ ]
itzam-1.0.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+