langchain-animeapi 0.1.0__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.
- langchain_animeapi/__init__.py +14 -0
- langchain_animeapi/_version.py +1 -0
- langchain_animeapi/py.typed +0 -0
- langchain_animeapi/tools.py +125 -0
- langchain_animeapi-0.1.0.dist-info/METADATA +117 -0
- langchain_animeapi-0.1.0.dist-info/RECORD +8 -0
- langchain_animeapi-0.1.0.dist-info/WHEEL +4 -0
- langchain_animeapi-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""AnimeAPI tool for LangChain and LangGraph agents (https://animeapi.ai).
|
|
2
|
+
|
|
3
|
+
Anime image generation at $0.003 per image.
|
|
4
|
+
|
|
5
|
+
from langchain_animeapi import AnimeAPIGenerateImage
|
|
6
|
+
|
|
7
|
+
tool = AnimeAPIGenerateImage() # reads ANIMEAPI_API_KEY
|
|
8
|
+
print(tool.invoke({"prompt": "a silver-haired girl reading under a cherry tree"}))
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from ._version import __version__
|
|
12
|
+
from .tools import AnimeAPIGenerateImage, AnimeAPIGenerateImageInput
|
|
13
|
+
|
|
14
|
+
__all__ = ["AnimeAPIGenerateImage", "AnimeAPIGenerateImageInput", "__version__"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
File without changes
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Dict, Literal, Optional, Tuple, Type
|
|
4
|
+
|
|
5
|
+
from animeapi import AnimeAPI, AnimeAPIError, AsyncAnimeAPI
|
|
6
|
+
from langchain_core.callbacks import AsyncCallbackManagerForToolRun, CallbackManagerForToolRun
|
|
7
|
+
from langchain_core.tools import BaseTool, ToolException
|
|
8
|
+
from pydantic import BaseModel, Field, SecretStr
|
|
9
|
+
|
|
10
|
+
PRICES_USD = {"anime": 0.003, "ultra": 0.01, "ultra-max": 0.02}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AnimeAPIGenerateImageInput(BaseModel):
|
|
14
|
+
"""What the model fills in when it calls the tool."""
|
|
15
|
+
|
|
16
|
+
prompt: str = Field(
|
|
17
|
+
description=(
|
|
18
|
+
"What to draw: subject, appearance, pose, setting, lighting. "
|
|
19
|
+
"Plain English works best. At most 1000 characters."
|
|
20
|
+
),
|
|
21
|
+
max_length=1000,
|
|
22
|
+
)
|
|
23
|
+
orientation: Literal["portrait", "square", "landscape"] = Field(
|
|
24
|
+
default="portrait",
|
|
25
|
+
description=(
|
|
26
|
+
"Shape of the image: portrait (characters, full body), square (avatars, icons) "
|
|
27
|
+
"or landscape (scenery, wide scenes). Same price for every shape."
|
|
28
|
+
),
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class AnimeAPIGenerateImage(BaseTool):
|
|
33
|
+
"""Generate an anime image with AnimeAPI: $0.003 per image on the default model.
|
|
34
|
+
|
|
35
|
+
Setup:
|
|
36
|
+
``pip install langchain-animeapi`` and set ``ANIMEAPI_API_KEY`` (keys start with
|
|
37
|
+
``ank_``; create one at https://animeapi.ai/dashboard/api-keys).
|
|
38
|
+
|
|
39
|
+
The model chooses the prompt and the shape. You choose the image model here, so an
|
|
40
|
+
agent cannot switch to a more expensive one:
|
|
41
|
+
|
|
42
|
+
tool = AnimeAPIGenerateImage(model="anime") # $0.003 per image (default)
|
|
43
|
+
tool = AnimeAPIGenerateImage(model="ultra") # $0.01 per image
|
|
44
|
+
|
|
45
|
+
The tool returns a short text for the model (the image URL, price and balance left)
|
|
46
|
+
and, as the artifact, a dict with ``image_url``, ``enhanced_prompt``, ``model``,
|
|
47
|
+
``orientation``, ``price_usd``, ``balance_usd`` and ``generation_time_ms``.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
name: str = "animeapi_generate_image"
|
|
51
|
+
description: str = (
|
|
52
|
+
"Generate one anime-style image from a text description and return its URL. "
|
|
53
|
+
"Costs $0.003 per image on the default model. Describe the subject, appearance, pose, "
|
|
54
|
+
"setting and lighting in plain English. "
|
|
55
|
+
"Adult content is not available; explicit prompts are refused and not charged."
|
|
56
|
+
)
|
|
57
|
+
args_schema: Type[BaseModel] = AnimeAPIGenerateImageInput
|
|
58
|
+
response_format: Literal["content", "content_and_artifact"] = "content_and_artifact"
|
|
59
|
+
handle_tool_error: bool = True
|
|
60
|
+
|
|
61
|
+
api_key: Optional[SecretStr] = Field(default=None, description="Defaults to ANIMEAPI_API_KEY.")
|
|
62
|
+
model: Literal["anime", "ultra", "ultra-max"] = "anime"
|
|
63
|
+
enhance: bool = True
|
|
64
|
+
timeout: float = 120.0
|
|
65
|
+
max_retries: int = 2
|
|
66
|
+
|
|
67
|
+
def _client_kwargs(self) -> Dict[str, Any]:
|
|
68
|
+
return {
|
|
69
|
+
"api_key": self.api_key.get_secret_value() if self.api_key else None,
|
|
70
|
+
"timeout": self.timeout,
|
|
71
|
+
"max_retries": self.max_retries,
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
def _result(self, image: Any, orientation: str) -> Tuple[str, Dict[str, Any]]:
|
|
75
|
+
price = PRICES_USD[self.model]
|
|
76
|
+
content = (
|
|
77
|
+
f"Image generated: {image.image_url}\n"
|
|
78
|
+
f"Model {self.model}, {orientation}, ${price}. Balance left: ${image.balance_usd}.\n"
|
|
79
|
+
"The link is not permanent storage; download the image to keep it."
|
|
80
|
+
)
|
|
81
|
+
artifact = {
|
|
82
|
+
"image_url": image.image_url,
|
|
83
|
+
"enhanced_prompt": image.enhanced_prompt,
|
|
84
|
+
"model": self.model,
|
|
85
|
+
"orientation": orientation,
|
|
86
|
+
"price_usd": price,
|
|
87
|
+
"balance_usd": image.balance_usd,
|
|
88
|
+
"generation_time_ms": image.generation_time_ms,
|
|
89
|
+
}
|
|
90
|
+
return content, artifact
|
|
91
|
+
|
|
92
|
+
@staticmethod
|
|
93
|
+
def _tool_error(e: AnimeAPIError) -> ToolException:
|
|
94
|
+
# The API's own message, e.g. why a prompt was refused or that the balance is empty.
|
|
95
|
+
return ToolException(f"AnimeAPI: {e.message} ({e.code or e.status})")
|
|
96
|
+
|
|
97
|
+
def _run(
|
|
98
|
+
self,
|
|
99
|
+
prompt: str,
|
|
100
|
+
orientation: str = "portrait",
|
|
101
|
+
run_manager: Optional[CallbackManagerForToolRun] = None,
|
|
102
|
+
) -> Tuple[str, Dict[str, Any]]:
|
|
103
|
+
try:
|
|
104
|
+
with AnimeAPI(**self._client_kwargs()) as client:
|
|
105
|
+
image = client.images.generate(
|
|
106
|
+
prompt, model=self.model, orientation=orientation, enhance=self.enhance # type: ignore[arg-type]
|
|
107
|
+
)
|
|
108
|
+
except AnimeAPIError as e:
|
|
109
|
+
raise self._tool_error(e) from e
|
|
110
|
+
return self._result(image, orientation)
|
|
111
|
+
|
|
112
|
+
async def _arun(
|
|
113
|
+
self,
|
|
114
|
+
prompt: str,
|
|
115
|
+
orientation: str = "portrait",
|
|
116
|
+
run_manager: Optional[AsyncCallbackManagerForToolRun] = None,
|
|
117
|
+
) -> Tuple[str, Dict[str, Any]]:
|
|
118
|
+
try:
|
|
119
|
+
async with AsyncAnimeAPI(**self._client_kwargs()) as client:
|
|
120
|
+
image = await client.images.generate(
|
|
121
|
+
prompt, model=self.model, orientation=orientation, enhance=self.enhance # type: ignore[arg-type]
|
|
122
|
+
)
|
|
123
|
+
except AnimeAPIError as e:
|
|
124
|
+
raise self._tool_error(e) from e
|
|
125
|
+
return self._result(image, orientation)
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: langchain-animeapi
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: AnimeAPI tool for LangChain and LangGraph agents: anime image generation at $0.003 per image.
|
|
5
|
+
Project-URL: Homepage, https://animeapi.ai
|
|
6
|
+
Project-URL: Documentation, https://animeapi.ai/docs
|
|
7
|
+
Project-URL: Repository, https://github.com/animeapi-ai/langchain-animeapi
|
|
8
|
+
Project-URL: Issues, https://github.com/animeapi-ai/langchain-animeapi/issues
|
|
9
|
+
Author-email: AnimeAPI <contact@animeapi.ai>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: agent,ai,anime,animeapi,image-generation,langchain,langgraph,text-to-image,tool
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Requires-Dist: animeapi-sdk<1,>=0.1.2
|
|
22
|
+
Requires-Dist: langchain-core<2,>=0.3.0
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# langchain-animeapi
|
|
26
|
+
|
|
27
|
+
[AnimeAPI](https://animeapi.ai) tool for [LangChain](https://github.com/langchain-ai/langchain) and LangGraph agents: anime image generation at **$0.003 per image**, one of the cheapest anime image APIs. Prepaid balance from $5 that never expires, no subscription; new accounts get 3 free images without a card.
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install langchain-animeapi
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Create a key (it starts with `ank_`) at <https://animeapi.ai/dashboard/api-keys> and set it as `ANIMEAPI_API_KEY`.
|
|
34
|
+
|
|
35
|
+
## Quick start
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from langchain_animeapi import AnimeAPIGenerateImage
|
|
39
|
+
|
|
40
|
+
tool = AnimeAPIGenerateImage() # reads ANIMEAPI_API_KEY
|
|
41
|
+
|
|
42
|
+
print(tool.invoke({"prompt": "a shrine maiden sweeping autumn leaves", "orientation": "landscape"}))
|
|
43
|
+
# Image generated: https://animeapi.ai/i/….webp
|
|
44
|
+
# Model anime, landscape, $0.003. Balance left: $4.997.
|
|
45
|
+
# The link is not permanent storage; download the image to keep it.
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## With an agent
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from langchain.agents import create_agent
|
|
52
|
+
from langchain_animeapi import AnimeAPIGenerateImage
|
|
53
|
+
|
|
54
|
+
agent = create_agent(model, tools=[AnimeAPIGenerateImage()]) # model: your chat model
|
|
55
|
+
result = agent.invoke({"messages": [{"role": "user", "content": "Draw a chibi cat barista, square."}]})
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Any chat model that supports tool calling works: `model.bind_tools([AnimeAPIGenerateImage()])`. The model writes the prompt and picks the shape. When it calls the tool, the `ToolMessage` carries a short text for the model and the details as `artifact`:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
msg = tool.invoke({
|
|
62
|
+
"type": "tool_call", "id": "call_1", "name": "animeapi_generate_image",
|
|
63
|
+
"args": {"prompt": "a chibi cat barista holding a latte", "orientation": "square"},
|
|
64
|
+
})
|
|
65
|
+
msg.artifact["image_url"] # https://animeapi.ai/i/….webp
|
|
66
|
+
msg.artifact["enhanced_prompt"] # the tags the model received
|
|
67
|
+
msg.artifact["price_usd"] # 0.003
|
|
68
|
+
msg.artifact["balance_usd"] # balance after this image
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`ainvoke` works the same way for async agents.
|
|
72
|
+
|
|
73
|
+
## Options
|
|
74
|
+
|
|
75
|
+
| Option | Default | What it does |
|
|
76
|
+
| --- | --- | --- |
|
|
77
|
+
| `api_key` | `ANIMEAPI_API_KEY` | Your key. Stored as a secret, so it does not show up in `repr()` or traces. |
|
|
78
|
+
| `model` | `"anime"` | `"anime"` $0.003, `"ultra"` $0.01, `"ultra-max"` $0.02 per image. Set by you, not by the language model, so an agent cannot pick a more expensive one. |
|
|
79
|
+
| `enhance` | `True` | Rewrites plain words into tags for the anime model. |
|
|
80
|
+
| `timeout` | `120` | Seconds to wait for one request. |
|
|
81
|
+
| `max_retries` | `2` | Retries for failures the API marks as retryable. |
|
|
82
|
+
|
|
83
|
+
What the language model can set per call: `prompt` (up to 1000 characters) and `orientation` (`portrait`, `square` or `landscape`; same price).
|
|
84
|
+
|
|
85
|
+
AnimeAPI does not generate adult content. Explicit prompts are refused and not charged.
|
|
86
|
+
|
|
87
|
+
## Errors
|
|
88
|
+
|
|
89
|
+
A failed call does not raise inside your agent: the tool answers with an error `ToolMessage` carrying AnimeAPI's own message, so the model can tell the user what happened (prompt refused, wrong key, empty balance). Set `handle_tool_error=False` to get a `ToolException` instead.
|
|
90
|
+
|
|
91
|
+
## Keeping the image
|
|
92
|
+
|
|
93
|
+
Image URLs are not permanent storage. Download what you want to keep, for example with the SDK this package is built on:
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
from animeapi import AnimeAPI
|
|
97
|
+
|
|
98
|
+
AnimeAPI().download(msg.artifact["image_url"]).save("cat.webp")
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Prices
|
|
102
|
+
|
|
103
|
+
| Model | Price per image | Use it for |
|
|
104
|
+
| --- | --- | --- |
|
|
105
|
+
| `anime` | **$0.003** | Characters, portraits, avatars, scenes. About 4 seconds. |
|
|
106
|
+
| `ultra` | $0.01 | Long, detailed prompts; legible text in the image. |
|
|
107
|
+
| `ultra-max` | $0.02 | Highest detail. |
|
|
108
|
+
|
|
109
|
+
## Tested
|
|
110
|
+
|
|
111
|
+
Run against the live API with `langchain-core` 1.8.0 on 2026-11-12: a direct call, a tool call (returns a `ToolMessage` with the artifact), an async tool call, a refused prompt and a wrong key (both come back as error messages, uncharged).
|
|
112
|
+
|
|
113
|
+
## Links
|
|
114
|
+
|
|
115
|
+
- AnimeAPI: <https://animeapi.ai> · [Docs](https://animeapi.ai/docs) · [API reference](https://animeapi.ai/docs/api-reference)
|
|
116
|
+
- Python SDK: [animeapi-sdk](https://github.com/animeapi-ai/animeapi-python) · MCP server for agents: <https://animeapi.ai/mcp>
|
|
117
|
+
- Licence: [MIT](LICENSE)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
langchain_animeapi/__init__.py,sha256=NVuKyi9yErLnaTrU0pXs4whuBK5aHxIcO_iRpH1Edq8,517
|
|
2
|
+
langchain_animeapi/_version.py,sha256=kUR5RAFc7HCeiqdlX36dZOHkUI5wI6V_43RpEcD8b-0,22
|
|
3
|
+
langchain_animeapi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
langchain_animeapi/tools.py,sha256=YXiK2sDRyA8nZVypt6sh8qq_Hu9hdSkpsT1EUsTjVFE,5165
|
|
5
|
+
langchain_animeapi-0.1.0.dist-info/METADATA,sha256=AMEGMsgg2zoLPLJG44Q3hVuk_IwuMWelPUFakDs9ZdM,5194
|
|
6
|
+
langchain_animeapi-0.1.0.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
|
|
7
|
+
langchain_animeapi-0.1.0.dist-info/licenses/LICENSE,sha256=x52k-d-9cV329_Nys60vf7-YJDtqve5_SugPMT8tbaQ,1065
|
|
8
|
+
langchain_animeapi-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 AnimeAPI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|