kodari 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.
- kodari-1.0.0/.idea/.gitignore +5 -0
- kodari-1.0.0/.idea/inspectionProfiles/profiles_settings.xml +6 -0
- kodari-1.0.0/.idea/kodaripython.iml +10 -0
- kodari-1.0.0/.idea/misc.xml +7 -0
- kodari-1.0.0/.idea/modules.xml +8 -0
- kodari-1.0.0/.idea/workspace.xml +46 -0
- kodari-1.0.0/PKG-INFO +145 -0
- kodari-1.0.0/README.md +129 -0
- kodari-1.0.0/kodari/__init__.py +17 -0
- kodari-1.0.0/kodari/client.py +194 -0
- kodari-1.0.0/kodari/credentials.py +14 -0
- kodari-1.0.0/kodari/exceptions.py +14 -0
- kodari-1.0.0/kodari/models/__init__.py +17 -0
- kodari-1.0.0/kodari/models/responses.py +80 -0
- kodari-1.0.0/pyproject.toml +27 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<module type="PYTHON_MODULE" version="4">
|
|
3
|
+
<component name="NewModuleRootManager">
|
|
4
|
+
<content url="file://$MODULE_DIR$">
|
|
5
|
+
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
|
6
|
+
</content>
|
|
7
|
+
<orderEntry type="jdk" jdkName="Python 3.13 (kodaripython)" jdkType="Python SDK" />
|
|
8
|
+
<orderEntry type="sourceFolder" forTests="false" />
|
|
9
|
+
</component>
|
|
10
|
+
</module>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="Black">
|
|
4
|
+
<option name="sdkName" value="Python 3.13 (kodaripython)" />
|
|
5
|
+
</component>
|
|
6
|
+
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.13 (kodaripython)" project-jdk-type="Python SDK" />
|
|
7
|
+
</project>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="ProjectModuleManager">
|
|
4
|
+
<modules>
|
|
5
|
+
<module fileurl="file://$PROJECT_DIR$/.idea/kodaripython.iml" filepath="$PROJECT_DIR$/.idea/kodaripython.iml" />
|
|
6
|
+
</modules>
|
|
7
|
+
</component>
|
|
8
|
+
</project>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<project version="4">
|
|
3
|
+
<component name="AutoImportSettings">
|
|
4
|
+
<option name="autoReloadType" value="SELECTIVE" />
|
|
5
|
+
</component>
|
|
6
|
+
<component name="ChangeListManager">
|
|
7
|
+
<list default="true" id="ef19ca08-3230-4d7f-9a7b-462908395666" name="Changes" comment="" />
|
|
8
|
+
<option name="SHOW_DIALOG" value="false" />
|
|
9
|
+
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
|
10
|
+
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
|
11
|
+
<option name="LAST_RESOLUTION" value="IGNORE" />
|
|
12
|
+
</component>
|
|
13
|
+
<component name="ProjectColorInfo"><![CDATA[{
|
|
14
|
+
"associatedIndex": 5
|
|
15
|
+
}]]></component>
|
|
16
|
+
<component name="ProjectId" id="3BcQjIqEAu0J4IWeTPoXMrGq6xw" />
|
|
17
|
+
<component name="ProjectViewState">
|
|
18
|
+
<option name="hideEmptyMiddlePackages" value="true" />
|
|
19
|
+
<option name="showLibraryContents" value="true" />
|
|
20
|
+
</component>
|
|
21
|
+
<component name="PropertiesComponent"><![CDATA[{
|
|
22
|
+
"keyToString": {
|
|
23
|
+
"ModuleVcsDetector.initialDetectionPerformed": "true",
|
|
24
|
+
"RunOnceActivity.ShowReadmeOnStart": "true",
|
|
25
|
+
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
|
|
26
|
+
"ignore.virus.scanning.warn.message": "true"
|
|
27
|
+
}
|
|
28
|
+
}]]></component>
|
|
29
|
+
<component name="SharedIndexes">
|
|
30
|
+
<attachedChunks>
|
|
31
|
+
<set>
|
|
32
|
+
<option value="bundled-python-sdk-164cda30dcd9-0af03a5fa574-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-252.26830.99" />
|
|
33
|
+
</set>
|
|
34
|
+
</attachedChunks>
|
|
35
|
+
</component>
|
|
36
|
+
<component name="TaskManager">
|
|
37
|
+
<task active="true" id="Default" summary="Default task">
|
|
38
|
+
<changelist id="ef19ca08-3230-4d7f-9a7b-462908395666" name="Changes" comment="" />
|
|
39
|
+
<created>1774786011668</created>
|
|
40
|
+
<option name="number" value="Default" />
|
|
41
|
+
<option name="presentableId" value="Default" />
|
|
42
|
+
<updated>1774786011668</updated>
|
|
43
|
+
</task>
|
|
44
|
+
<servers />
|
|
45
|
+
</component>
|
|
46
|
+
</project>
|
kodari-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: kodari
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Official Python SDK for the Kodari API
|
|
5
|
+
Project-URL: Homepage, https://kodari.ai
|
|
6
|
+
Project-URL: Repository, https://github.com/kodari-ai/kodaripython
|
|
7
|
+
Author-email: Kodari <support@kodari.ai>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
Keywords: ai,api,kodari,moderation
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Requires-Python: >=3.10
|
|
14
|
+
Requires-Dist: httpx>=0.28.0
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
# kodaripython
|
|
18
|
+
|
|
19
|
+
Official Python SDK for the [Kodari API](https://kodari.ai). Fully async, built on httpx.
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install kodari
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
Get your API key at [kodari.ai/api-keys](https://kodari.ai/api-keys).
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
import asyncio
|
|
33
|
+
from kodari import KodariClient, KodariCredentials
|
|
34
|
+
|
|
35
|
+
async def main():
|
|
36
|
+
async with KodariClient(KodariCredentials("kod-your-api-key")) as client:
|
|
37
|
+
result = await client.moderate("hello everyone")
|
|
38
|
+
|
|
39
|
+
if not result.safe:
|
|
40
|
+
print(f"Flagged: {result.category}") # toxicity, threat, doxxing, advertising, spam
|
|
41
|
+
print(f"Severity: {result.severity}") # low, medium, high
|
|
42
|
+
|
|
43
|
+
asyncio.run(main())
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Moderation
|
|
47
|
+
|
|
48
|
+
Multilingual chat moderation AI that classifies messages for toxicity, threats, doxxing, spam, and advertising.
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
result = await client.moderate("some message")
|
|
52
|
+
|
|
53
|
+
result.safe # True/False
|
|
54
|
+
result.category # none, toxicity, threat, doxxing, advertising, spam
|
|
55
|
+
result.severity # none, low, medium, high
|
|
56
|
+
|
|
57
|
+
# Convenience checks
|
|
58
|
+
result.is_toxic
|
|
59
|
+
result.is_threat
|
|
60
|
+
result.is_doxxing
|
|
61
|
+
result.is_advertising
|
|
62
|
+
result.is_spam
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Batch Moderation
|
|
66
|
+
|
|
67
|
+
Send 5+ messages for a 50% discount on token cost.
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
results = await client.moderate_batch([
|
|
71
|
+
"hello everyone",
|
|
72
|
+
"you're terrible at this game",
|
|
73
|
+
"check out my store at scam.com",
|
|
74
|
+
])
|
|
75
|
+
|
|
76
|
+
for r in results:
|
|
77
|
+
print(f"safe={r.safe} category={r.category}")
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Generic Model Execution
|
|
81
|
+
|
|
82
|
+
Call any model by name, even ones added after your SDK version:
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
response = await client.execute("moderation", "your input")
|
|
86
|
+
|
|
87
|
+
response.kodari_model # "moderation"
|
|
88
|
+
response.tokens_cost # 10
|
|
89
|
+
response.result # raw dict
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Session Flow (Code Generation)
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
session = await client.create_session("My Plugin", "minecraft", "plugin", "claude-sonnet-4-5")
|
|
96
|
+
gen = await client.generate(session.id, "Make a plugin that does X")
|
|
97
|
+
compiled = await client.compile(session.id)
|
|
98
|
+
jar_bytes = await client.download_jar(compiled.jar_id)
|
|
99
|
+
|
|
100
|
+
with open("plugin.jar", "wb") as f:
|
|
101
|
+
f.write(jar_bytes)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Other Endpoints
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
# Get current user
|
|
108
|
+
user = await client.get_me()
|
|
109
|
+
print(user.name, user.email)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Error Handling
|
|
113
|
+
|
|
114
|
+
All exceptions extend `KodariException`:
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from kodari import (
|
|
118
|
+
KodariAuthenticationException,
|
|
119
|
+
KodariRateLimitException,
|
|
120
|
+
KodariInsufficientTokensException,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
result = await client.moderate("message")
|
|
125
|
+
except KodariAuthenticationException:
|
|
126
|
+
print("Invalid API key")
|
|
127
|
+
except KodariRateLimitException:
|
|
128
|
+
print("Rate limited, slow down")
|
|
129
|
+
except KodariInsufficientTokensException:
|
|
130
|
+
print("Not enough tokens")
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Custom Configuration
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
client = KodariClient(
|
|
137
|
+
KodariCredentials("kod-your-api-key"),
|
|
138
|
+
base_url="http://localhost:8080",
|
|
139
|
+
user_agent="my-app/2.0",
|
|
140
|
+
)
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Requirements
|
|
144
|
+
|
|
145
|
+
Python 3.10+
|
kodari-1.0.0/README.md
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# kodaripython
|
|
2
|
+
|
|
3
|
+
Official Python SDK for the [Kodari API](https://kodari.ai). Fully async, built on httpx.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install kodari
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
Get your API key at [kodari.ai/api-keys](https://kodari.ai/api-keys).
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
import asyncio
|
|
17
|
+
from kodari import KodariClient, KodariCredentials
|
|
18
|
+
|
|
19
|
+
async def main():
|
|
20
|
+
async with KodariClient(KodariCredentials("kod-your-api-key")) as client:
|
|
21
|
+
result = await client.moderate("hello everyone")
|
|
22
|
+
|
|
23
|
+
if not result.safe:
|
|
24
|
+
print(f"Flagged: {result.category}") # toxicity, threat, doxxing, advertising, spam
|
|
25
|
+
print(f"Severity: {result.severity}") # low, medium, high
|
|
26
|
+
|
|
27
|
+
asyncio.run(main())
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Moderation
|
|
31
|
+
|
|
32
|
+
Multilingual chat moderation AI that classifies messages for toxicity, threats, doxxing, spam, and advertising.
|
|
33
|
+
|
|
34
|
+
```python
|
|
35
|
+
result = await client.moderate("some message")
|
|
36
|
+
|
|
37
|
+
result.safe # True/False
|
|
38
|
+
result.category # none, toxicity, threat, doxxing, advertising, spam
|
|
39
|
+
result.severity # none, low, medium, high
|
|
40
|
+
|
|
41
|
+
# Convenience checks
|
|
42
|
+
result.is_toxic
|
|
43
|
+
result.is_threat
|
|
44
|
+
result.is_doxxing
|
|
45
|
+
result.is_advertising
|
|
46
|
+
result.is_spam
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Batch Moderation
|
|
50
|
+
|
|
51
|
+
Send 5+ messages for a 50% discount on token cost.
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
results = await client.moderate_batch([
|
|
55
|
+
"hello everyone",
|
|
56
|
+
"you're terrible at this game",
|
|
57
|
+
"check out my store at scam.com",
|
|
58
|
+
])
|
|
59
|
+
|
|
60
|
+
for r in results:
|
|
61
|
+
print(f"safe={r.safe} category={r.category}")
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Generic Model Execution
|
|
65
|
+
|
|
66
|
+
Call any model by name, even ones added after your SDK version:
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
response = await client.execute("moderation", "your input")
|
|
70
|
+
|
|
71
|
+
response.kodari_model # "moderation"
|
|
72
|
+
response.tokens_cost # 10
|
|
73
|
+
response.result # raw dict
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Session Flow (Code Generation)
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
session = await client.create_session("My Plugin", "minecraft", "plugin", "claude-sonnet-4-5")
|
|
80
|
+
gen = await client.generate(session.id, "Make a plugin that does X")
|
|
81
|
+
compiled = await client.compile(session.id)
|
|
82
|
+
jar_bytes = await client.download_jar(compiled.jar_id)
|
|
83
|
+
|
|
84
|
+
with open("plugin.jar", "wb") as f:
|
|
85
|
+
f.write(jar_bytes)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Other Endpoints
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
# Get current user
|
|
92
|
+
user = await client.get_me()
|
|
93
|
+
print(user.name, user.email)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Error Handling
|
|
97
|
+
|
|
98
|
+
All exceptions extend `KodariException`:
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
from kodari import (
|
|
102
|
+
KodariAuthenticationException,
|
|
103
|
+
KodariRateLimitException,
|
|
104
|
+
KodariInsufficientTokensException,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
try:
|
|
108
|
+
result = await client.moderate("message")
|
|
109
|
+
except KodariAuthenticationException:
|
|
110
|
+
print("Invalid API key")
|
|
111
|
+
except KodariRateLimitException:
|
|
112
|
+
print("Rate limited, slow down")
|
|
113
|
+
except KodariInsufficientTokensException:
|
|
114
|
+
print("Not enough tokens")
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Custom Configuration
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
client = KodariClient(
|
|
121
|
+
KodariCredentials("kod-your-api-key"),
|
|
122
|
+
base_url="http://localhost:8080",
|
|
123
|
+
user_agent="my-app/2.0",
|
|
124
|
+
)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Requirements
|
|
128
|
+
|
|
129
|
+
Python 3.10+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from kodari.client import KodariClient
|
|
2
|
+
from kodari.credentials import KodariCredentials
|
|
3
|
+
from kodari.exceptions import (
|
|
4
|
+
KodariException,
|
|
5
|
+
KodariAuthenticationException,
|
|
6
|
+
KodariRateLimitException,
|
|
7
|
+
KodariInsufficientTokensException,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"KodariClient",
|
|
12
|
+
"KodariCredentials",
|
|
13
|
+
"KodariException",
|
|
14
|
+
"KodariAuthenticationException",
|
|
15
|
+
"KodariRateLimitException",
|
|
16
|
+
"KodariInsufficientTokensException",
|
|
17
|
+
]
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
from typing import List, Optional
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
|
|
5
|
+
from kodari.credentials import KodariCredentials
|
|
6
|
+
from kodari.exceptions import (
|
|
7
|
+
KodariAuthenticationException,
|
|
8
|
+
KodariException,
|
|
9
|
+
KodariInsufficientTokensException,
|
|
10
|
+
KodariRateLimitException,
|
|
11
|
+
)
|
|
12
|
+
from kodari.models.responses import (
|
|
13
|
+
CompileResponse,
|
|
14
|
+
GenerateResponse,
|
|
15
|
+
ModelResponse,
|
|
16
|
+
ModerationResult,
|
|
17
|
+
SessionResponse,
|
|
18
|
+
UserResponse,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
_DEFAULT_BASE_URL = "https://api.kodari.ai"
|
|
22
|
+
_API_PREFIX = "/api/v1"
|
|
23
|
+
_DEFAULT_USER_AGENT = "kodari-python/1.0.0"
|
|
24
|
+
_MODERATION = "moderation"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class KodariClient:
|
|
28
|
+
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
credentials: KodariCredentials,
|
|
32
|
+
base_url: str = _DEFAULT_BASE_URL,
|
|
33
|
+
user_agent: str = _DEFAULT_USER_AGENT,
|
|
34
|
+
):
|
|
35
|
+
self._credentials = credentials
|
|
36
|
+
self._base_url = base_url
|
|
37
|
+
self._client = httpx.AsyncClient(
|
|
38
|
+
headers={
|
|
39
|
+
"X-API-Key": credentials.api_key,
|
|
40
|
+
"User-Agent": user_agent,
|
|
41
|
+
},
|
|
42
|
+
timeout=60.0, # cf
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
async def close(self):
|
|
46
|
+
await self._client.aclose()
|
|
47
|
+
|
|
48
|
+
async def __aenter__(self):
|
|
49
|
+
return self
|
|
50
|
+
|
|
51
|
+
async def __aexit__(self, *args):
|
|
52
|
+
await self.close()
|
|
53
|
+
|
|
54
|
+
# Normal endpoints
|
|
55
|
+
|
|
56
|
+
async def get_me(self) -> UserResponse:
|
|
57
|
+
data = await self._get("/users/me")
|
|
58
|
+
return UserResponse(
|
|
59
|
+
id=data["id"],
|
|
60
|
+
provider_id=data.get("providerId", ""),
|
|
61
|
+
email=data.get("email", ""),
|
|
62
|
+
name=data.get("name", ""),
|
|
63
|
+
image_url=data.get("imageUrl", ""),
|
|
64
|
+
role=data.get("role", ""),
|
|
65
|
+
discord_id=data.get("discordId", ""),
|
|
66
|
+
bio=data.get("bio", ""),
|
|
67
|
+
created_at=data.get("createdAt", ""),
|
|
68
|
+
banned=data.get("banned", False),
|
|
69
|
+
banned_reason=data.get("bannedReason", ""),
|
|
70
|
+
settings=data.get("settings", {}),
|
|
71
|
+
new_account=data.get("newAccount", False),
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
async def create_session(
|
|
75
|
+
self,
|
|
76
|
+
name: str,
|
|
77
|
+
game_type: str,
|
|
78
|
+
category: str,
|
|
79
|
+
ai_model: str,
|
|
80
|
+
) -> SessionResponse:
|
|
81
|
+
data = await self._post("/sessions", {
|
|
82
|
+
"name": name,
|
|
83
|
+
"gameType": game_type,
|
|
84
|
+
"category": category,
|
|
85
|
+
"aiModel": ai_model,
|
|
86
|
+
})
|
|
87
|
+
return SessionResponse(
|
|
88
|
+
id=data["id"],
|
|
89
|
+
name=data["name"],
|
|
90
|
+
game_type=data.get("gameType", ""),
|
|
91
|
+
category=data.get("category", ""),
|
|
92
|
+
status=data.get("status", ""),
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
async def generate(
|
|
96
|
+
self,
|
|
97
|
+
session_id: str,
|
|
98
|
+
message: str,
|
|
99
|
+
has_dependencies: bool = False,
|
|
100
|
+
) -> GenerateResponse:
|
|
101
|
+
data = await self._post(f"/sessions/{session_id}/generate", {
|
|
102
|
+
"message": message,
|
|
103
|
+
"hasDependencies": has_dependencies,
|
|
104
|
+
})
|
|
105
|
+
return GenerateResponse(
|
|
106
|
+
session_id=data["sessionId"],
|
|
107
|
+
success=data["success"],
|
|
108
|
+
message=data["message"],
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
async def compile(self, session_id: str) -> CompileResponse:
|
|
112
|
+
data = await self._post(f"/sessions/{session_id}/compile", {})
|
|
113
|
+
return CompileResponse(
|
|
114
|
+
session_id=data["sessionId"],
|
|
115
|
+
success=data["success"],
|
|
116
|
+
jar_id=data.get("jarId"),
|
|
117
|
+
plugin_name=data.get("pluginName"),
|
|
118
|
+
jar_size=data.get("jarSize"),
|
|
119
|
+
error=data.get("error"),
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
async def download_jar(self, jar_id: int) -> bytes:
|
|
123
|
+
url = f"{self._base_url}{_API_PREFIX}/download/jar/{jar_id}"
|
|
124
|
+
response = await self._client.post(
|
|
125
|
+
url,
|
|
126
|
+
json={},
|
|
127
|
+
headers={"Content-Type": "application/json"},
|
|
128
|
+
)
|
|
129
|
+
self._check_status(response.status_code, response.text)
|
|
130
|
+
return response.content
|
|
131
|
+
|
|
132
|
+
# Kodari Specialized Models
|
|
133
|
+
|
|
134
|
+
async def moderate(self, message: str) -> ModerationResult:
|
|
135
|
+
response = await self.execute(_MODERATION, message)
|
|
136
|
+
result = response.result
|
|
137
|
+
return ModerationResult(
|
|
138
|
+
safe=result["safe"],
|
|
139
|
+
category=result["category"],
|
|
140
|
+
severity=result["severity"],
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
async def moderate_batch(self, messages: List[str]) -> List[ModerationResult]:
|
|
144
|
+
response = await self.execute_batch(_MODERATION, messages)
|
|
145
|
+
return [
|
|
146
|
+
ModerationResult(
|
|
147
|
+
safe=r["safe"],
|
|
148
|
+
category=r["category"],
|
|
149
|
+
severity=r["severity"],
|
|
150
|
+
index=r.get("index", -1),
|
|
151
|
+
)
|
|
152
|
+
for r in response.result
|
|
153
|
+
]
|
|
154
|
+
|
|
155
|
+
async def execute(self, model: str, input: str) -> ModelResponse:
|
|
156
|
+
data = await self._post(f"/models/{model}", {"input": input})
|
|
157
|
+
return ModelResponse(
|
|
158
|
+
kodari_model=data["kodariModel"],
|
|
159
|
+
tokens_cost=data["tokensCost"],
|
|
160
|
+
result=data["result"],
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
async def execute_batch(self, model: str, inputs: List[str]) -> ModelResponse:
|
|
164
|
+
data = await self._post(f"/models/{model}/batch", {"inputs": inputs})
|
|
165
|
+
return ModelResponse(
|
|
166
|
+
kodari_model=data["kodariModel"],
|
|
167
|
+
tokens_cost=data["tokensCost"],
|
|
168
|
+
result=data["result"],
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
# Generic API access
|
|
172
|
+
|
|
173
|
+
async def _get(self, path: str) -> dict:
|
|
174
|
+
url = f"{self._base_url}{_API_PREFIX}{path}"
|
|
175
|
+
response = await self._client.get(url)
|
|
176
|
+
self._check_status(response.status_code, response.text)
|
|
177
|
+
return response.json()
|
|
178
|
+
|
|
179
|
+
async def _post(self, path: str, body: dict) -> dict:
|
|
180
|
+
url = f"{self._base_url}{_API_PREFIX}{path}"
|
|
181
|
+
response = await self._client.post(url, json=body)
|
|
182
|
+
self._check_status(response.status_code, response.text)
|
|
183
|
+
return response.json()
|
|
184
|
+
|
|
185
|
+
@staticmethod
|
|
186
|
+
def _check_status(status: int, body: str):
|
|
187
|
+
if status == 401:
|
|
188
|
+
raise KodariAuthenticationException("Invalid or expired API key")
|
|
189
|
+
if status == 429:
|
|
190
|
+
raise KodariRateLimitException("Rate limit exceeded")
|
|
191
|
+
if status == 402:
|
|
192
|
+
raise KodariInsufficientTokensException("Insufficient tokens")
|
|
193
|
+
if status != 200:
|
|
194
|
+
raise KodariException(f"API error (HTTP {status}): {body}")
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
class KodariCredentials:
|
|
2
|
+
|
|
3
|
+
def __init__(self, api_key: str):
|
|
4
|
+
if not api_key:
|
|
5
|
+
raise ValueError("api_key must not be None or empty")
|
|
6
|
+
|
|
7
|
+
if not api_key.startswith("kod-"):
|
|
8
|
+
raise ValueError("Invalid API key format. Keys must start with 'kod-'")
|
|
9
|
+
|
|
10
|
+
self._api_key = api_key
|
|
11
|
+
|
|
12
|
+
@property
|
|
13
|
+
def api_key(self) -> str:
|
|
14
|
+
return self._api_key
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from kodari.models.responses import (
|
|
2
|
+
UserResponse,
|
|
3
|
+
SessionResponse,
|
|
4
|
+
GenerateResponse,
|
|
5
|
+
CompileResponse,
|
|
6
|
+
ModelResponse,
|
|
7
|
+
ModerationResult,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"UserResponse",
|
|
12
|
+
"SessionResponse",
|
|
13
|
+
"GenerateResponse",
|
|
14
|
+
"CompileResponse",
|
|
15
|
+
"ModelResponse",
|
|
16
|
+
"ModerationResult",
|
|
17
|
+
]
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Any, Optional
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@dataclass
|
|
6
|
+
class UserResponse:
|
|
7
|
+
id: str
|
|
8
|
+
provider_id: str
|
|
9
|
+
email: str
|
|
10
|
+
name: str
|
|
11
|
+
image_url: str
|
|
12
|
+
role: str
|
|
13
|
+
discord_id: str
|
|
14
|
+
bio: str
|
|
15
|
+
created_at: str
|
|
16
|
+
banned: bool
|
|
17
|
+
banned_reason: str
|
|
18
|
+
settings: dict
|
|
19
|
+
new_account: bool
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class SessionResponse:
|
|
24
|
+
id: str
|
|
25
|
+
name: str
|
|
26
|
+
game_type: str
|
|
27
|
+
category: str
|
|
28
|
+
status: str
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class GenerateResponse:
|
|
33
|
+
session_id: str
|
|
34
|
+
success: bool
|
|
35
|
+
message: str
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class CompileResponse:
|
|
40
|
+
session_id: str
|
|
41
|
+
success: bool
|
|
42
|
+
jar_id: Optional[int] = None
|
|
43
|
+
plugin_name: Optional[str] = None
|
|
44
|
+
jar_size: Optional[int] = None
|
|
45
|
+
error: Optional[str] = None
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class ModelResponse:
|
|
50
|
+
kodari_model: str
|
|
51
|
+
tokens_cost: int
|
|
52
|
+
result: Any
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass
|
|
56
|
+
class ModerationResult:
|
|
57
|
+
safe: bool
|
|
58
|
+
category: str
|
|
59
|
+
severity: str
|
|
60
|
+
index: int = -1
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def is_toxic(self) -> bool:
|
|
64
|
+
return self.category == "toxicity"
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def is_threat(self) -> bool:
|
|
68
|
+
return self.category == "threat"
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def is_doxxing(self) -> bool:
|
|
72
|
+
return self.category == "doxxing"
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def is_advertising(self) -> bool:
|
|
76
|
+
return self.category == "advertising"
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def is_spam(self) -> bool:
|
|
80
|
+
return self.category == "spam"
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "kodari"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Official Python SDK for the Kodari API"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Kodari", email = "support@kodari.ai" }
|
|
14
|
+
]
|
|
15
|
+
keywords = ["kodari", "ai", "moderation", "api"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
]
|
|
21
|
+
dependencies = [
|
|
22
|
+
"httpx>=0.28.0",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Homepage = "https://kodari.ai"
|
|
27
|
+
Repository = "https://github.com/kodari-ai/kodaripython"
|