imho 0.2.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.
- imho-0.2.0/.gitignore +10 -0
- imho-0.2.0/LICENSE +21 -0
- imho-0.2.0/PKG-INFO +202 -0
- imho-0.2.0/README.md +175 -0
- imho-0.2.0/pyproject.toml +59 -0
- imho-0.2.0/src/imho/__init__.py +31 -0
- imho-0.2.0/src/imho/_version.py +1 -0
- imho-0.2.0/src/imho/client.py +597 -0
- imho-0.2.0/src/imho/errors.py +66 -0
- imho-0.2.0/src/imho/py.typed +0 -0
- imho-0.2.0/src/imho/types.py +297 -0
- imho-0.2.0/tests/test_client.py +323 -0
imho-0.2.0/.gitignore
ADDED
imho-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 imho.run
|
|
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.
|
imho-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: imho
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Python client for the imho.run API: Steam games like any game, game facts, and finding a game from a description.
|
|
5
|
+
Project-URL: Homepage, https://imho.run/developers
|
|
6
|
+
Project-URL: Documentation, https://github.com/0x216/imho-mcp/tree/main/python#readme
|
|
7
|
+
Project-URL: Repository, https://github.com/0x216/imho-mcp
|
|
8
|
+
Project-URL: Issues, https://github.com/0x216/imho-mcp/issues
|
|
9
|
+
Author-email: "imho.run" <admin@imho.run>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: game-recommendations,games,imho,mcp,recommender-system,steam
|
|
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: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Topic :: Games/Entertainment
|
|
19
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
Requires-Dist: httpx<1,>=0.24
|
|
23
|
+
Provides-Extra: test
|
|
24
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == 'test'
|
|
25
|
+
Requires-Dist: pytest>=7; extra == 'test'
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# imho (Python client for imho.run)
|
|
29
|
+
|
|
30
|
+
A small typed client for the [imho.run](https://imho.run) API for AI assistants:
|
|
31
|
+
Steam games like any game you name, recommendations from several games with
|
|
32
|
+
filters, facts about one game, what is trending and newly released on Steam,
|
|
33
|
+
title search, and identifying a game from a description. It wraps the public
|
|
34
|
+
REST endpoints under `https://imho.run/api/agent/` and the MCP endpoint
|
|
35
|
+
`https://imho.run/mcp`. The API is free, read-only and needs no key.
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install imho
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Requires Python 3.9+ and [httpx](https://www.python-httpx.org/). Source,
|
|
42
|
+
issues and the MCP setup for assistants: <https://github.com/0x216/imho-mcp>.
|
|
43
|
+
|
|
44
|
+
## Usage
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from imho import ImhoClient
|
|
48
|
+
|
|
49
|
+
with ImhoClient() as imho:
|
|
50
|
+
picks = imho.games_like("Hollow Knight", n=3)
|
|
51
|
+
for game in picks["results"]:
|
|
52
|
+
print(game["rank"], game["name"], "-", game["why"], game["price"]["text"])
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
1 Hollow Knight: Silksong - Also Metroidvania and Souls-like, like Hollow Knight. 19.99 USD
|
|
57
|
+
2 Ori and the Blind Forest: Definitive Edition - Also Metroidvania, like Hollow Knight. 4.99 USD
|
|
58
|
+
3 Nine Sols - Metroidvania with Sekiro-style parry combat and Taoist myth. 14.99 USD
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
(Output from 2026-10-04. Rankings and prices change.)
|
|
62
|
+
|
|
63
|
+
### games_like
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
imho.games_like(
|
|
67
|
+
"Stardew Valley", # name (typos, Russian titles OK), Steam appid or Steam store URL
|
|
68
|
+
n=10, # 1..20
|
|
69
|
+
lang="en", # "en" or "ru": language of the `why` lines
|
|
70
|
+
free=False, # only free-to-play games
|
|
71
|
+
coop=False, # True = any co-op, "online" or "local" (same screen / split screen)
|
|
72
|
+
steam_deck=None, # "verified" or "playable" (playable or better)
|
|
73
|
+
)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Returns a dict with `seed` (the game that was matched), `other_matches`,
|
|
77
|
+
`results` (each with `rank`, `appid`, `name`, `url`, `steam_url`, `why`,
|
|
78
|
+
`year`, `price`, `steam_deck`, `reviews`, `genres`), `list_url` and
|
|
79
|
+
`attribution`.
|
|
80
|
+
|
|
81
|
+
### recommend
|
|
82
|
+
|
|
83
|
+
For several games, more filters, or what the user wants in their own words:
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
recs = imho.recommend(
|
|
87
|
+
["Stardew Valley", "Terraria"], # 1-3 seed games
|
|
88
|
+
n=10, # 1..24
|
|
89
|
+
preferences="cozy farming, no horror", # free text, read as Steam tags
|
|
90
|
+
coop=True, # also "online" / "local"
|
|
91
|
+
exclude=["pvp", "grind"], # pvp, microtransactions, hard, grind, early_access, vr_only
|
|
92
|
+
exclude_tags=["Anime"], # Steam tags to leave out
|
|
93
|
+
year_min=2015, year_max=None, # release years (year_max defaults to this year)
|
|
94
|
+
upcoming=False, # True: include unreleased games
|
|
95
|
+
popularity_bias=-0.5, # -1 more niche ... 1 more popular
|
|
96
|
+
free=False, steam_deck=None, lang="en",
|
|
97
|
+
)
|
|
98
|
+
for game in recs["results"]:
|
|
99
|
+
print(game["name"], "-", game["why"])
|
|
100
|
+
recs["preferences"]["prefer_tags"], recs["preferences"]["avoid_tags"]
|
|
101
|
+
# (['Farming Sim'], ['Horror'])
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
To refine after the user reacts, call again with `liked=[...]` (fills free
|
|
105
|
+
seed slots, 3 seeds in all) and `disliked=[...]` (never recommended again).
|
|
106
|
+
|
|
107
|
+
### trending, new_releases, search_games
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
imho.trending(kind="rising", n=10) # or kind="breakouts" for new games taking off
|
|
111
|
+
imho.new_releases(n=10) # well-rated releases of the last 30 days
|
|
112
|
+
imho.new_releases(upcoming=True, coop=True)
|
|
113
|
+
imho.search_games("hollow kn", n=5) # appid, year and links per match
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Trending picks carry `reviews_week` and `reviews_growth_pct`; new releases
|
|
117
|
+
carry `release_date`. While trending data is still being collected,
|
|
118
|
+
`trending()` returns `status: "collecting"` and an empty `results`.
|
|
119
|
+
|
|
120
|
+
### game_facts
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
facts = imho.game_facts("Hades")["game"]
|
|
124
|
+
facts["summary"]["length"], facts["summary"]["difficulty"], facts["steam_deck"]
|
|
125
|
+
# ('massive', 'challenging', 'verified')
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Year, developers, genres, top tags, price, Steam Deck status, review numbers,
|
|
129
|
+
Steam's short description and, when imho.run has one, a summary mined from
|
|
130
|
+
player reviews (difficulty, length, session shape, co-op, hooks, dealbreakers).
|
|
131
|
+
|
|
132
|
+
### find_game_by_description
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
hit = imho.find_game_by_description(
|
|
136
|
+
"you play a cat in a cyberpunk city with a little drone",
|
|
137
|
+
platform="pc", # pc, playstation, xbox, nintendo, sega, mobile, browser, arcade
|
|
138
|
+
year_min=None, year_max=None,
|
|
139
|
+
perspective=None, # first, third, top_down, side
|
|
140
|
+
)
|
|
141
|
+
hit["confidence"], hit["results"][0]["name"] # ('medium', 'Stray')
|
|
142
|
+
hit["find_game_url"] # imho.run/find-game with the description filled in
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
This one runs a language model on the server. It takes several seconds and is
|
|
146
|
+
limited to 3 calls a minute and 20 a day per IP, so use it only when the title
|
|
147
|
+
is unknown.
|
|
148
|
+
|
|
149
|
+
### Async
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
import asyncio
|
|
153
|
+
from imho import AsyncImhoClient
|
|
154
|
+
|
|
155
|
+
async def main() -> None:
|
|
156
|
+
async with AsyncImhoClient() as imho:
|
|
157
|
+
facts = await imho.game_facts("1145360")
|
|
158
|
+
print(facts["game"]["name"])
|
|
159
|
+
|
|
160
|
+
asyncio.run(main())
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Any MCP tool
|
|
164
|
+
|
|
165
|
+
`call_tool(name, arguments)` calls any tool on the MCP server and returns its
|
|
166
|
+
structured result, and `list_tools()` returns the tool definitions. Use them
|
|
167
|
+
for tools added to the server after this release.
|
|
168
|
+
|
|
169
|
+
## Errors
|
|
170
|
+
|
|
171
|
+
| Exception | When |
|
|
172
|
+
| --- | --- |
|
|
173
|
+
| `NotFoundError` | No game matched the query (HTTP 404). |
|
|
174
|
+
| `BadRequestError` | A parameter is out of range or the query is empty (400/422), or an unknown MCP tool. |
|
|
175
|
+
| `RateLimitError` | Over the limit (HTTP 429). `retry_after` holds the seconds to wait when known. |
|
|
176
|
+
| `DisabledError` | The API is switched off on the server (503). |
|
|
177
|
+
| `ToolError` | An MCP tool returned `isError` for another reason. |
|
|
178
|
+
|
|
179
|
+
All of them subclass `ImhoError`, which has `detail`, `code` and `status`.
|
|
180
|
+
|
|
181
|
+
## Limits and attribution
|
|
182
|
+
|
|
183
|
+
30 requests a minute and 1,000 a day per IP; `find_game_by_description` 3 a
|
|
184
|
+
minute and 20 a day; `recommend` 10 new (uncached) combinations a minute and
|
|
185
|
+
200 a day. Responses are cached on the server.
|
|
186
|
+
|
|
187
|
+
If you show the results to people, credit imho.run ("Recommendations by
|
|
188
|
+
imho.run") and link each game's `url`. Every response has an `attribution`
|
|
189
|
+
field with that wording.
|
|
190
|
+
|
|
191
|
+
## Development
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
cd python
|
|
195
|
+
uv run --with pytest --with pytest-asyncio --with-editable . pytest -q
|
|
196
|
+
IMHO_LIVE=1 uv run --with pytest --with pytest-asyncio --with-editable . pytest -q -m live # hits the real API
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## License
|
|
200
|
+
|
|
201
|
+
MIT. The client is MIT-licensed; the imho.run service and its data are covered
|
|
202
|
+
by the [imho.run terms](https://imho.run/terms).
|
imho-0.2.0/README.md
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# imho (Python client for imho.run)
|
|
2
|
+
|
|
3
|
+
A small typed client for the [imho.run](https://imho.run) API for AI assistants:
|
|
4
|
+
Steam games like any game you name, recommendations from several games with
|
|
5
|
+
filters, facts about one game, what is trending and newly released on Steam,
|
|
6
|
+
title search, and identifying a game from a description. It wraps the public
|
|
7
|
+
REST endpoints under `https://imho.run/api/agent/` and the MCP endpoint
|
|
8
|
+
`https://imho.run/mcp`. The API is free, read-only and needs no key.
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pip install imho
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Requires Python 3.9+ and [httpx](https://www.python-httpx.org/). Source,
|
|
15
|
+
issues and the MCP setup for assistants: <https://github.com/0x216/imho-mcp>.
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
from imho import ImhoClient
|
|
21
|
+
|
|
22
|
+
with ImhoClient() as imho:
|
|
23
|
+
picks = imho.games_like("Hollow Knight", n=3)
|
|
24
|
+
for game in picks["results"]:
|
|
25
|
+
print(game["rank"], game["name"], "-", game["why"], game["price"]["text"])
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
1 Hollow Knight: Silksong - Also Metroidvania and Souls-like, like Hollow Knight. 19.99 USD
|
|
30
|
+
2 Ori and the Blind Forest: Definitive Edition - Also Metroidvania, like Hollow Knight. 4.99 USD
|
|
31
|
+
3 Nine Sols - Metroidvania with Sekiro-style parry combat and Taoist myth. 14.99 USD
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
(Output from 2026-10-04. Rankings and prices change.)
|
|
35
|
+
|
|
36
|
+
### games_like
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
imho.games_like(
|
|
40
|
+
"Stardew Valley", # name (typos, Russian titles OK), Steam appid or Steam store URL
|
|
41
|
+
n=10, # 1..20
|
|
42
|
+
lang="en", # "en" or "ru": language of the `why` lines
|
|
43
|
+
free=False, # only free-to-play games
|
|
44
|
+
coop=False, # True = any co-op, "online" or "local" (same screen / split screen)
|
|
45
|
+
steam_deck=None, # "verified" or "playable" (playable or better)
|
|
46
|
+
)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Returns a dict with `seed` (the game that was matched), `other_matches`,
|
|
50
|
+
`results` (each with `rank`, `appid`, `name`, `url`, `steam_url`, `why`,
|
|
51
|
+
`year`, `price`, `steam_deck`, `reviews`, `genres`), `list_url` and
|
|
52
|
+
`attribution`.
|
|
53
|
+
|
|
54
|
+
### recommend
|
|
55
|
+
|
|
56
|
+
For several games, more filters, or what the user wants in their own words:
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
recs = imho.recommend(
|
|
60
|
+
["Stardew Valley", "Terraria"], # 1-3 seed games
|
|
61
|
+
n=10, # 1..24
|
|
62
|
+
preferences="cozy farming, no horror", # free text, read as Steam tags
|
|
63
|
+
coop=True, # also "online" / "local"
|
|
64
|
+
exclude=["pvp", "grind"], # pvp, microtransactions, hard, grind, early_access, vr_only
|
|
65
|
+
exclude_tags=["Anime"], # Steam tags to leave out
|
|
66
|
+
year_min=2015, year_max=None, # release years (year_max defaults to this year)
|
|
67
|
+
upcoming=False, # True: include unreleased games
|
|
68
|
+
popularity_bias=-0.5, # -1 more niche ... 1 more popular
|
|
69
|
+
free=False, steam_deck=None, lang="en",
|
|
70
|
+
)
|
|
71
|
+
for game in recs["results"]:
|
|
72
|
+
print(game["name"], "-", game["why"])
|
|
73
|
+
recs["preferences"]["prefer_tags"], recs["preferences"]["avoid_tags"]
|
|
74
|
+
# (['Farming Sim'], ['Horror'])
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
To refine after the user reacts, call again with `liked=[...]` (fills free
|
|
78
|
+
seed slots, 3 seeds in all) and `disliked=[...]` (never recommended again).
|
|
79
|
+
|
|
80
|
+
### trending, new_releases, search_games
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
imho.trending(kind="rising", n=10) # or kind="breakouts" for new games taking off
|
|
84
|
+
imho.new_releases(n=10) # well-rated releases of the last 30 days
|
|
85
|
+
imho.new_releases(upcoming=True, coop=True)
|
|
86
|
+
imho.search_games("hollow kn", n=5) # appid, year and links per match
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Trending picks carry `reviews_week` and `reviews_growth_pct`; new releases
|
|
90
|
+
carry `release_date`. While trending data is still being collected,
|
|
91
|
+
`trending()` returns `status: "collecting"` and an empty `results`.
|
|
92
|
+
|
|
93
|
+
### game_facts
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
facts = imho.game_facts("Hades")["game"]
|
|
97
|
+
facts["summary"]["length"], facts["summary"]["difficulty"], facts["steam_deck"]
|
|
98
|
+
# ('massive', 'challenging', 'verified')
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Year, developers, genres, top tags, price, Steam Deck status, review numbers,
|
|
102
|
+
Steam's short description and, when imho.run has one, a summary mined from
|
|
103
|
+
player reviews (difficulty, length, session shape, co-op, hooks, dealbreakers).
|
|
104
|
+
|
|
105
|
+
### find_game_by_description
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
hit = imho.find_game_by_description(
|
|
109
|
+
"you play a cat in a cyberpunk city with a little drone",
|
|
110
|
+
platform="pc", # pc, playstation, xbox, nintendo, sega, mobile, browser, arcade
|
|
111
|
+
year_min=None, year_max=None,
|
|
112
|
+
perspective=None, # first, third, top_down, side
|
|
113
|
+
)
|
|
114
|
+
hit["confidence"], hit["results"][0]["name"] # ('medium', 'Stray')
|
|
115
|
+
hit["find_game_url"] # imho.run/find-game with the description filled in
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
This one runs a language model on the server. It takes several seconds and is
|
|
119
|
+
limited to 3 calls a minute and 20 a day per IP, so use it only when the title
|
|
120
|
+
is unknown.
|
|
121
|
+
|
|
122
|
+
### Async
|
|
123
|
+
|
|
124
|
+
```python
|
|
125
|
+
import asyncio
|
|
126
|
+
from imho import AsyncImhoClient
|
|
127
|
+
|
|
128
|
+
async def main() -> None:
|
|
129
|
+
async with AsyncImhoClient() as imho:
|
|
130
|
+
facts = await imho.game_facts("1145360")
|
|
131
|
+
print(facts["game"]["name"])
|
|
132
|
+
|
|
133
|
+
asyncio.run(main())
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Any MCP tool
|
|
137
|
+
|
|
138
|
+
`call_tool(name, arguments)` calls any tool on the MCP server and returns its
|
|
139
|
+
structured result, and `list_tools()` returns the tool definitions. Use them
|
|
140
|
+
for tools added to the server after this release.
|
|
141
|
+
|
|
142
|
+
## Errors
|
|
143
|
+
|
|
144
|
+
| Exception | When |
|
|
145
|
+
| --- | --- |
|
|
146
|
+
| `NotFoundError` | No game matched the query (HTTP 404). |
|
|
147
|
+
| `BadRequestError` | A parameter is out of range or the query is empty (400/422), or an unknown MCP tool. |
|
|
148
|
+
| `RateLimitError` | Over the limit (HTTP 429). `retry_after` holds the seconds to wait when known. |
|
|
149
|
+
| `DisabledError` | The API is switched off on the server (503). |
|
|
150
|
+
| `ToolError` | An MCP tool returned `isError` for another reason. |
|
|
151
|
+
|
|
152
|
+
All of them subclass `ImhoError`, which has `detail`, `code` and `status`.
|
|
153
|
+
|
|
154
|
+
## Limits and attribution
|
|
155
|
+
|
|
156
|
+
30 requests a minute and 1,000 a day per IP; `find_game_by_description` 3 a
|
|
157
|
+
minute and 20 a day; `recommend` 10 new (uncached) combinations a minute and
|
|
158
|
+
200 a day. Responses are cached on the server.
|
|
159
|
+
|
|
160
|
+
If you show the results to people, credit imho.run ("Recommendations by
|
|
161
|
+
imho.run") and link each game's `url`. Every response has an `attribution`
|
|
162
|
+
field with that wording.
|
|
163
|
+
|
|
164
|
+
## Development
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
cd python
|
|
168
|
+
uv run --with pytest --with pytest-asyncio --with-editable . pytest -q
|
|
169
|
+
IMHO_LIVE=1 uv run --with pytest --with pytest-asyncio --with-editable . pytest -q -m live # hits the real API
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## License
|
|
173
|
+
|
|
174
|
+
MIT. The client is MIT-licensed; the imho.run service and its data are covered
|
|
175
|
+
by the [imho.run terms](https://imho.run/terms).
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.26"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "imho"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Python client for the imho.run API: Steam games like any game, game facts, and finding a game from a description."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
requires-python = ">=3.9"
|
|
13
|
+
authors = [{ name = "imho.run", email = "admin@imho.run" }]
|
|
14
|
+
keywords = ["steam", "games", "game-recommendations", "recommender-system", "mcp", "imho"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 4 - Beta",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
21
|
+
"Topic :: Games/Entertainment",
|
|
22
|
+
"Topic :: Internet :: WWW/HTTP",
|
|
23
|
+
"Typing :: Typed",
|
|
24
|
+
]
|
|
25
|
+
dependencies = ["httpx>=0.24,<1"]
|
|
26
|
+
|
|
27
|
+
[project.optional-dependencies]
|
|
28
|
+
test = ["pytest>=7", "pytest-asyncio>=0.21"]
|
|
29
|
+
|
|
30
|
+
[project.urls]
|
|
31
|
+
Homepage = "https://imho.run/developers"
|
|
32
|
+
Documentation = "https://github.com/0x216/imho-mcp/tree/main/python#readme"
|
|
33
|
+
Repository = "https://github.com/0x216/imho-mcp"
|
|
34
|
+
Issues = "https://github.com/0x216/imho-mcp/issues"
|
|
35
|
+
|
|
36
|
+
[tool.hatch.version]
|
|
37
|
+
path = "src/imho/_version.py"
|
|
38
|
+
|
|
39
|
+
[tool.hatch.build.targets.wheel]
|
|
40
|
+
packages = ["src/imho"]
|
|
41
|
+
|
|
42
|
+
[tool.hatch.build.targets.sdist]
|
|
43
|
+
include = ["src/imho", "tests", "README.md", "LICENSE"]
|
|
44
|
+
|
|
45
|
+
[tool.pytest.ini_options]
|
|
46
|
+
testpaths = ["tests"]
|
|
47
|
+
asyncio_mode = "auto"
|
|
48
|
+
markers = ["live: calls the real imho.run API (set IMHO_LIVE=1)"]
|
|
49
|
+
|
|
50
|
+
[tool.ruff]
|
|
51
|
+
target-version = "py39"
|
|
52
|
+
line-length = 100
|
|
53
|
+
|
|
54
|
+
[tool.ruff.lint]
|
|
55
|
+
select = ["E", "F", "I", "B"]
|
|
56
|
+
|
|
57
|
+
[tool.mypy]
|
|
58
|
+
strict = true
|
|
59
|
+
python_version = "3.10"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Python client for the imho.run agent API (Steam game recommendations).
|
|
2
|
+
|
|
3
|
+
>>> from imho import ImhoClient
|
|
4
|
+
>>> with ImhoClient() as imho:
|
|
5
|
+
... for pick in imho.games_like("Hollow Knight", n=3)["results"]:
|
|
6
|
+
... print(pick["rank"], pick["name"], "-", pick["why"])
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from ._version import __version__
|
|
10
|
+
from .client import DEFAULT_BASE_URL, AsyncImhoClient, ImhoClient
|
|
11
|
+
from .errors import (
|
|
12
|
+
BadRequestError,
|
|
13
|
+
DisabledError,
|
|
14
|
+
ImhoError,
|
|
15
|
+
NotFoundError,
|
|
16
|
+
RateLimitError,
|
|
17
|
+
ToolError,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"DEFAULT_BASE_URL",
|
|
22
|
+
"AsyncImhoClient",
|
|
23
|
+
"BadRequestError",
|
|
24
|
+
"DisabledError",
|
|
25
|
+
"ImhoClient",
|
|
26
|
+
"ImhoError",
|
|
27
|
+
"NotFoundError",
|
|
28
|
+
"RateLimitError",
|
|
29
|
+
"ToolError",
|
|
30
|
+
"__version__",
|
|
31
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.0"
|