simforge-py 0.1.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.
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: simforge-py
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Simforge client for provider-based API calls
|
|
5
|
+
Author: Harvest Team
|
|
6
|
+
Requires-Python: >=3.11,<3.13
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
10
|
+
Requires-Dist: minijinja (>=2.12.0,<3.0.0)
|
|
11
|
+
Requires-Dist: openai (>=1.42.0,<2.0.0)
|
|
12
|
+
Requires-Dist: requests (>=2.32.3,<3.0.0)
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# Kirby
|
|
16
|
+
|
|
17
|
+
Kirby client for provider-based API calls.
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
For local development:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
cd kirby
|
|
25
|
+
poetry install
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Or install as an editable package from the parent directory:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
poetry add --editable ../kirby
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from kirby import Kirby
|
|
38
|
+
|
|
39
|
+
client = Kirby(
|
|
40
|
+
provider="openAI",
|
|
41
|
+
key="your-api-key",
|
|
42
|
+
model="gpt-4",
|
|
43
|
+
kirby_url="https://your-kirby-instance.com", # Optional, can use KIRBY_URL env var
|
|
44
|
+
kirby_user_id="user-id", # Optional, can use KIRBY_USER_ID env var
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
result = client.call("method_name", arg1="value1", arg2="value2")
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Configuration
|
|
51
|
+
|
|
52
|
+
The `kirby_url` and `kirby_user_id` can be provided either:
|
|
53
|
+
- As constructor arguments
|
|
54
|
+
- As environment variables (`KIRBY_URL` and `KIRBY_USER_ID`)
|
|
55
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Kirby
|
|
2
|
+
|
|
3
|
+
Kirby client for provider-based API calls.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
For local development:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
cd kirby
|
|
11
|
+
poetry install
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Or install as an editable package from the parent directory:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
poetry add --editable ../kirby
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
from kirby import Kirby
|
|
24
|
+
|
|
25
|
+
client = Kirby(
|
|
26
|
+
provider="openAI",
|
|
27
|
+
key="your-api-key",
|
|
28
|
+
model="gpt-4",
|
|
29
|
+
kirby_url="https://your-kirby-instance.com", # Optional, can use KIRBY_URL env var
|
|
30
|
+
kirby_user_id="user-id", # Optional, can use KIRBY_USER_ID env var
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
result = client.call("method_name", arg1="value1", arg2="value2")
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Configuration
|
|
37
|
+
|
|
38
|
+
The `kirby_url` and `kirby_user_id` can be provided either:
|
|
39
|
+
- As constructor arguments
|
|
40
|
+
- As environment variables (`KIRBY_URL` and `KIRBY_USER_ID`)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "simforge-py"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Simforge client for provider-based API calls"
|
|
5
|
+
authors = ["Harvest Team"]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
packages = [{include = "simforge"}]
|
|
8
|
+
|
|
9
|
+
[tool.poetry.dependencies]
|
|
10
|
+
python = ">=3.11,<3.13"
|
|
11
|
+
requests = "^2.32.3"
|
|
12
|
+
minijinja = "^2.12.0"
|
|
13
|
+
openai = "^1.42.0"
|
|
14
|
+
|
|
15
|
+
[build-system]
|
|
16
|
+
requires = ["poetry-core"]
|
|
17
|
+
build-backend = "poetry.core.masonry.api"
|
|
18
|
+
|
|
19
|
+
[tool.black]
|
|
20
|
+
line-length = 88
|
|
21
|
+
target-version = ['py311']
|
|
22
|
+
include = '\.pyi?$'
|
|
23
|
+
|
|
24
|
+
[tool.isort]
|
|
25
|
+
profile = "black"
|
|
26
|
+
multi_line_output = 3
|
|
27
|
+
line_length = 88
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"""Kirby client for provider-based API calls."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from typing import Any, Optional
|
|
5
|
+
|
|
6
|
+
import requests
|
|
7
|
+
from minijinja import Environment
|
|
8
|
+
from openai import OpenAI
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Kirby:
|
|
14
|
+
"""Client for making provider-based API calls."""
|
|
15
|
+
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
provider: str,
|
|
19
|
+
key: str,
|
|
20
|
+
model: str,
|
|
21
|
+
kirby_url: Optional[str] = None,
|
|
22
|
+
kirby_user_id: Optional[str] = None,
|
|
23
|
+
):
|
|
24
|
+
"""Initialize the Kirby client.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
provider: The provider name (e.g., 'openAI')
|
|
28
|
+
key: The API key for the provider
|
|
29
|
+
model: The model name to use
|
|
30
|
+
kirby_url: The base URL for the Kirby API (can also be set via KIRBY_URL env var)
|
|
31
|
+
kirby_user_id: The user ID for Kirby API (can also be set via KIRBY_USER_ID env var)
|
|
32
|
+
"""
|
|
33
|
+
self.provider = provider
|
|
34
|
+
self.key = key
|
|
35
|
+
self.model = model
|
|
36
|
+
self.kirby_url = kirby_url
|
|
37
|
+
self.kirby_user_id = kirby_user_id
|
|
38
|
+
|
|
39
|
+
def call(self, method_name: str, **kwargs: Any) -> Any:
|
|
40
|
+
"""Call a method with the given arguments.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
method_name: The name of the method to call
|
|
44
|
+
**kwargs: Keyword arguments to pass to the method
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
The result of the OpenAI API call
|
|
48
|
+
|
|
49
|
+
Raises:
|
|
50
|
+
ValueError: If KIRBY_URL or KIRBY_USER_ID is not set, or if no prompt is found
|
|
51
|
+
"""
|
|
52
|
+
import os
|
|
53
|
+
|
|
54
|
+
# Get kirby_url and kirby_user_id from instance or environment
|
|
55
|
+
kirby_url = self.kirby_url or os.getenv("KIRBY_URL")
|
|
56
|
+
kirby_user_id = self.kirby_user_id or os.getenv("KIRBY_USER_ID")
|
|
57
|
+
|
|
58
|
+
if not kirby_url:
|
|
59
|
+
raise ValueError(
|
|
60
|
+
"KIRBY_URL must be provided or set as environment variable"
|
|
61
|
+
)
|
|
62
|
+
if not kirby_user_id:
|
|
63
|
+
raise ValueError(
|
|
64
|
+
"KIRBY_USER_ID must be provided or set as environment variable"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Call find_or_create endpoint
|
|
68
|
+
url = f"{kirby_url}/api/functions/find_or_create"
|
|
69
|
+
payload = {
|
|
70
|
+
"userId": kirby_user_id,
|
|
71
|
+
"name": method_name,
|
|
72
|
+
"variables": kwargs,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
response = requests.post(
|
|
77
|
+
url,
|
|
78
|
+
json=payload,
|
|
79
|
+
headers={"Content-Type": "application/json"},
|
|
80
|
+
timeout=30,
|
|
81
|
+
)
|
|
82
|
+
response.raise_for_status()
|
|
83
|
+
function_result = response.json()
|
|
84
|
+
except requests.exceptions.RequestException as e:
|
|
85
|
+
logger.error(f"Error calling Kirby endpoint: {e}")
|
|
86
|
+
if hasattr(e, "response") and e.response is not None:
|
|
87
|
+
logger.error(f"Response status: {e.response.status_code}")
|
|
88
|
+
logger.error(f"Response body: {e.response.text}")
|
|
89
|
+
raise
|
|
90
|
+
|
|
91
|
+
# Check if we have a version prompt
|
|
92
|
+
version_prompt = function_result.get("versionPrompt")
|
|
93
|
+
function_id = function_result.get("id")
|
|
94
|
+
|
|
95
|
+
if not version_prompt:
|
|
96
|
+
# Build the function URL
|
|
97
|
+
function_url = f"{kirby_url}/functions/{function_id}" if function_id else kirby_url
|
|
98
|
+
raise ValueError(
|
|
99
|
+
f"No prompt found for this function. Please add a prompt at: {function_url}"
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# Get variables from the function result metadata and merge with kwargs
|
|
103
|
+
variables = function_result.get("metadata", {}).get("initialVariables", {})
|
|
104
|
+
# Merge with kwargs passed to the function
|
|
105
|
+
variables.update(kwargs)
|
|
106
|
+
|
|
107
|
+
# Template the prompt using minijinja
|
|
108
|
+
env = Environment()
|
|
109
|
+
templated_prompt = env.render_str(version_prompt, **variables)
|
|
110
|
+
logger.info(f"Templated prompt: {templated_prompt}")
|
|
111
|
+
|
|
112
|
+
# Call OpenAI with the templated prompt
|
|
113
|
+
openai_client = OpenAI(api_key=self.key)
|
|
114
|
+
completion = openai_client.chat.completions.create(
|
|
115
|
+
model=self.model,
|
|
116
|
+
messages=[{"role": "user", "content": templated_prompt}],
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
result = completion.choices[0].message.content
|
|
120
|
+
|
|
121
|
+
# Create trace with the result
|
|
122
|
+
function_version_id = function_result.get("versionId")
|
|
123
|
+
if function_version_id:
|
|
124
|
+
trace_url = f"{kirby_url}/api/functions/{function_id}/traces"
|
|
125
|
+
trace_payload = {
|
|
126
|
+
"functionVersionId": function_version_id,
|
|
127
|
+
"result": result,
|
|
128
|
+
"variables": variables,
|
|
129
|
+
"provider": self.provider.lower(),
|
|
130
|
+
"model": completion.model,
|
|
131
|
+
"usage": (
|
|
132
|
+
completion.usage.model_dump()
|
|
133
|
+
if hasattr(completion.usage, "model_dump")
|
|
134
|
+
else (
|
|
135
|
+
{
|
|
136
|
+
"prompt_tokens": getattr(
|
|
137
|
+
completion.usage, "prompt_tokens", 0
|
|
138
|
+
),
|
|
139
|
+
"completion_tokens": getattr(
|
|
140
|
+
completion.usage, "completion_tokens", 0
|
|
141
|
+
),
|
|
142
|
+
"total_tokens": getattr(
|
|
143
|
+
completion.usage, "total_tokens", 0
|
|
144
|
+
),
|
|
145
|
+
}
|
|
146
|
+
if completion.usage
|
|
147
|
+
else {}
|
|
148
|
+
)
|
|
149
|
+
),
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
try:
|
|
153
|
+
trace_response = requests.post(
|
|
154
|
+
trace_url,
|
|
155
|
+
json=trace_payload,
|
|
156
|
+
headers={"Content-Type": "application/json"},
|
|
157
|
+
timeout=30,
|
|
158
|
+
)
|
|
159
|
+
trace_response.raise_for_status()
|
|
160
|
+
logger.info(f"Trace created: {trace_response.json().get('id')}")
|
|
161
|
+
except requests.exceptions.RequestException as e:
|
|
162
|
+
logger.warning(f"Failed to create trace: {e}")
|
|
163
|
+
# Don't fail the whole function call if trace creation fails
|
|
164
|
+
|
|
165
|
+
return result
|