indoxrouter 0.1.12__tar.gz → 0.1.16__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.
@@ -1,10 +1,13 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: indoxrouter
3
- Version: 0.1.12
3
+ Version: 0.1.16
4
4
  Summary: A unified client for various AI providers
5
- Home-page: https://github.com/indoxrouter/indoxrouter
6
- Author: indoxRouter Team
7
- Author-email: ashkan.eskandari.dev@gmail.com
5
+ Author-email: indoxRouter Team <ashkan.eskandari.dev@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/indoxrouter/indoxrouter
8
+ Project-URL: Repository, https://github.com/indoxrouter/indoxrouter
9
+ Project-URL: Issues, https://github.com/indoxrouter/indoxrouter/issues
10
+ Keywords: ai,api,client,openai,anthropic,google,mistral
8
11
  Classifier: Development Status :: 3 - Alpha
9
12
  Classifier: Intended Audience :: Developers
10
13
  Classifier: License :: OSI Approved :: MIT License
@@ -18,27 +21,10 @@ Requires-Python: >=3.8
18
21
  Description-Content-Type: text/markdown
19
22
  Requires-Dist: requests>=2.25.0
20
23
  Requires-Dist: python-dotenv>=1.0.0
21
- Provides-Extra: dev
22
- Requires-Dist: pytest>=7.0.0; extra == "dev"
23
- Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
24
- Requires-Dist: black>=23.0.0; extra == "dev"
25
- Requires-Dist: isort>=5.0.0; extra == "dev"
26
- Requires-Dist: flake8>=6.0.0; extra == "dev"
27
- Requires-Dist: mypy>=1.0.0; extra == "dev"
28
- Dynamic: author
29
- Dynamic: author-email
30
- Dynamic: classifier
31
- Dynamic: description
32
- Dynamic: description-content-type
33
- Dynamic: home-page
34
- Dynamic: provides-extra
35
- Dynamic: requires-dist
36
- Dynamic: requires-python
37
- Dynamic: summary
38
24
 
39
25
  # IndoxRouter Client
40
26
 
41
- A unified client for various AI providers, including OpenAI, Anthropic, Google, and Mistral.
27
+ A unified client for various AI providers, including OpenAI, anthropic, Google, and Mistral.
42
28
 
43
29
  ## Features
44
30
 
@@ -1,6 +1,6 @@
1
1
  # IndoxRouter Client
2
2
 
3
- A unified client for various AI providers, including OpenAI, Anthropic, Google, and Mistral.
3
+ A unified client for various AI providers, including OpenAI, anthropic, Google, and Mistral.
4
4
 
5
5
  ## Features
6
6
 
@@ -41,4 +41,4 @@ python -c "from indoxrouter import Client; client = Client(api_key='your_api_key
41
41
 
42
42
  ## Note on API Keys
43
43
 
44
- The IndoxRouter API key is used to authenticate with the IndoxRouter server. You don't need to provide individual API keys for each provider (like OpenAI, Anthropic, etc.) as the IndoxRouter server handles that for you.
44
+ The IndoxRouter API key is used to authenticate with the IndoxRouter server. You don't need to provide individual API keys for each provider (like OpenAI, anthropic, etc.) as the IndoxRouter server handles that for you.
@@ -14,52 +14,6 @@
14
14
  "\n"
15
15
  ]
16
16
  },
17
- {
18
- "cell_type": "code",
19
- "execution_count": 2,
20
- "id": "479b6ce6",
21
- "metadata": {},
22
- "outputs": [
23
- {
24
- "name": "stdout",
25
- "output_type": "stream",
26
- "text": [
27
- "Collecting passlib\n",
28
- " Using cached passlib-1.7.4-py2.py3-none-any.whl.metadata (1.7 kB)\n",
29
- "Using cached passlib-1.7.4-py2.py3-none-any.whl (525 kB)\n",
30
- "Installing collected packages: passlib\n",
31
- "Successfully installed passlib-1.7.4\n",
32
- "Note: you may need to restart the kernel to use updated packages.\n"
33
- ]
34
- },
35
- {
36
- "name": "stderr",
37
- "output_type": "stream",
38
- "text": [
39
- "\n",
40
- "[notice] A new release of pip is available: 24.3.1 -> 25.0.1\n",
41
- "[notice] To update, run: python.exe -m pip install --upgrade pip\n"
42
- ]
43
- }
44
- ],
45
- "source": [
46
- "pip install passlib"
47
- ]
48
- },
49
- {
50
- "cell_type": "code",
51
- "execution_count": 1,
52
- "id": "e03bc1cc",
53
- "metadata": {},
54
- "outputs": [],
55
- "source": [
56
- "import sys\n",
57
- "import os\n",
58
- "module_path = os.path.abspath('E:/Codes/indoxRouter/')\n",
59
- "if module_path not in sys.path:\n",
60
- " sys.path.append(module_path)"
61
- ]
62
- },
63
17
  {
64
18
  "cell_type": "code",
65
19
  "execution_count": null,
@@ -1358,6 +1312,207 @@
1358
1312
  "\n",
1359
1313
  "For more information, refer to the [IndoxRouter documentation](https://docs.indoxrouter.com).\n"
1360
1314
  ]
1315
+ },
1316
+ {
1317
+ "cell_type": "code",
1318
+ "execution_count": 1,
1319
+ "id": "60ca0fd8",
1320
+ "metadata": {},
1321
+ "outputs": [
1322
+ {
1323
+ "name": "stdout",
1324
+ "output_type": "stream",
1325
+ "text": [
1326
+ "Requirement already satisfied: openai in e:\\anaconda\\lib\\site-packages (1.52.2)\n",
1327
+ "Requirement already satisfied: anyio<5,>=3.5.0 in e:\\anaconda\\lib\\site-packages (from openai) (4.2.0)\n",
1328
+ "Requirement already satisfied: distro<2,>=1.7.0 in e:\\anaconda\\lib\\site-packages (from openai) (1.9.0)\n",
1329
+ "Requirement already satisfied: httpx<1,>=0.23.0 in e:\\anaconda\\lib\\site-packages (from openai) (0.27.0)\n",
1330
+ "Requirement already satisfied: jiter<1,>=0.4.0 in e:\\anaconda\\lib\\site-packages (from openai) (0.6.1)\n",
1331
+ "Requirement already satisfied: pydantic<3,>=1.9.0 in c:\\users\\ashkan\\appdata\\roaming\\python\\python312\\site-packages (from openai) (2.9.2)\n",
1332
+ "Requirement already satisfied: sniffio in e:\\anaconda\\lib\\site-packages (from openai) (1.3.0)\n",
1333
+ "Requirement already satisfied: tqdm>4 in e:\\anaconda\\lib\\site-packages (from openai) (4.66.4)\n",
1334
+ "Requirement already satisfied: typing-extensions<5,>=4.11 in e:\\anaconda\\lib\\site-packages (from openai) (4.11.0)\n",
1335
+ "Requirement already satisfied: idna>=2.8 in e:\\anaconda\\lib\\site-packages (from anyio<5,>=3.5.0->openai) (3.7)\n",
1336
+ "Requirement already satisfied: certifi in e:\\anaconda\\lib\\site-packages (from httpx<1,>=0.23.0->openai) (2024.6.2)\n",
1337
+ "Requirement already satisfied: httpcore==1.* in e:\\anaconda\\lib\\site-packages (from httpx<1,>=0.23.0->openai) (1.0.5)\n",
1338
+ "Requirement already satisfied: h11<0.15,>=0.13 in e:\\anaconda\\lib\\site-packages (from httpcore==1.*->httpx<1,>=0.23.0->openai) (0.14.0)\n",
1339
+ "Requirement already satisfied: annotated-types>=0.6.0 in e:\\anaconda\\lib\\site-packages (from pydantic<3,>=1.9.0->openai) (0.6.0)\n",
1340
+ "Requirement already satisfied: pydantic-core==2.23.4 in e:\\anaconda\\lib\\site-packages (from pydantic<3,>=1.9.0->openai) (2.23.4)\n",
1341
+ "Requirement already satisfied: colorama in e:\\anaconda\\lib\\site-packages (from tqdm>4->openai) (0.4.6)\n"
1342
+ ]
1343
+ },
1344
+ {
1345
+ "name": "stderr",
1346
+ "output_type": "stream",
1347
+ "text": [
1348
+ "\n",
1349
+ "[notice] A new release of pip is available: 24.3.1 -> 25.1.1\n",
1350
+ "[notice] To update, run: python.exe -m pip install --upgrade pip\n"
1351
+ ]
1352
+ }
1353
+ ],
1354
+ "source": [
1355
+ "!pip install openai"
1356
+ ]
1357
+ },
1358
+ {
1359
+ "cell_type": "code",
1360
+ "execution_count": 2,
1361
+ "id": "4da1a40d",
1362
+ "metadata": {},
1363
+ "outputs": [],
1364
+ "source": [
1365
+ "from openai import OpenAI\n",
1366
+ "client = OpenAI(\n",
1367
+ " base_url=\"https://api.indoxrouter.com\",\n",
1368
+ " api_key=\"indox-vgfI5PEfUoZ6qufR2z6QqXFV4BHgxrez\",\n",
1369
+ ")\n"
1370
+ ]
1371
+ },
1372
+ {
1373
+ "cell_type": "code",
1374
+ "execution_count": 5,
1375
+ "id": "648eff2d",
1376
+ "metadata": {},
1377
+ "outputs": [],
1378
+ "source": [
1379
+ "from openai import OpenAI\n",
1380
+ "client = OpenAI(\n",
1381
+ " base_url=\"https://api.indoxrouter.com\",\n",
1382
+ " api_key=\"indox-vgfI5PEfUoZ6qufR2z6QqXFV4BHgxrez\",\n",
1383
+ ")\n",
1384
+ "\n",
1385
+ "completion = client.chat.completions.create(\n",
1386
+ " model=\"deepseek/deepseek-chat\",\n",
1387
+ " messages=[\n",
1388
+ " {\n",
1389
+ " \"role\": \"user\",\n",
1390
+ " \"content\": \"What is the meaning of life?\"\n",
1391
+ " }\n",
1392
+ " ]\n",
1393
+ ")"
1394
+ ]
1395
+ },
1396
+ {
1397
+ "cell_type": "code",
1398
+ "execution_count": 6,
1399
+ "id": "becbf4fa",
1400
+ "metadata": {},
1401
+ "outputs": [
1402
+ {
1403
+ "data": {
1404
+ "text/plain": [
1405
+ "ChatCompletion(id=None, choices=None, created=None, model='deepseek-chat', object=None, service_tier=None, system_fingerprint=None, usage=CompletionUsage(completion_tokens=None, prompt_tokens=None, total_tokens=None, completion_tokens_details=None, prompt_tokens_details=None, tokens_prompt=10, tokens_completion=348, tokens_total=358, cost=0.000706, latency=19.672869205474854, timestamp='2025-05-19T06:05:02.416455'), request_id='5ce677cf-0e30-49e3-a9d9-bf99f91edbb6', created_at='2025-05-19T06:05:02.434101', duration_ms=21821.611166000366, provider='deepseek', success=True, message='', raw_response=None, data='The meaning of life is one of the most profound and debated questions in philosophy, religion, and science. Different perspectives offer various answers:\\n\\n1. **Philosophical Perspectives:** \\n - **Existentialism (e.g., Sartre, Camus):** Life has no inherent meaning—we must create our own purpose through choices and actions. \\n - **Absurdism (Camus):** The search for meaning in a meaningless universe is absurd, but we must embrace life anyway. \\n - **Stoicism:** Meaning comes from virtue, wisdom, and living in harmony with nature. \\n\\n2. **Religious/Spiritual Views:** \\n - **Theistic (Christianity, Islam, etc.):** Life’s purpose is to serve, love, or unite with God (or the divine). \\n - **Eastern Traditions (Buddhism, Hinduism):** Meaning may involve enlightenment (breaking the cycle of suffering) or fulfilling dharma (duty). \\n\\n3. **Scientific Perspectives:** \\n - **Biological:** Life’s \"purpose\" is survival, reproduction, and passing on genes (evolutionary biology). \\n - **Cosmic:** From a physics standpoint, life may be a rare, fleeting phenomenon in an indifferent universe. \\n\\n4. **Personal/Subjective Meaning:** \\n Many find purpose in relationships, creativity, helping others, or personal growth. Viktor Frankl (Holocaust survivor) argued that meaning arises from suffering, love, and work. \\n\\n**Short answer?** There’s no universal meaning—it’s up to you to define it. What gives *your* life purpose? \\n\\nWould you like to explore a specific perspective?', finish_reason=None)"
1406
+ ]
1407
+ },
1408
+ "execution_count": 6,
1409
+ "metadata": {},
1410
+ "output_type": "execute_result"
1411
+ }
1412
+ ],
1413
+ "source": [
1414
+ "completion"
1415
+ ]
1416
+ },
1417
+ {
1418
+ "cell_type": "code",
1419
+ "execution_count": 7,
1420
+ "id": "410449d5",
1421
+ "metadata": {},
1422
+ "outputs": [
1423
+ {
1424
+ "name": "stdout",
1425
+ "output_type": "stream",
1426
+ "text": [
1427
+ "ChatCompletion(id=None, choices=None, created=None, model='deepseek-chat', object=None, service_tier=None, system_fingerprint=None, usage=CompletionUsage(completion_tokens=None, prompt_tokens=None, total_tokens=None, completion_tokens_details=None, prompt_tokens_details=None, tokens_prompt=10, tokens_completion=348, tokens_total=358, cost=0.000706, latency=19.672869205474854, timestamp='2025-05-19T06:05:02.416455'), request_id='5ce677cf-0e30-49e3-a9d9-bf99f91edbb6', created_at='2025-05-19T06:05:02.434101', duration_ms=21821.611166000366, provider='deepseek', success=True, message='', raw_response=None, data='The meaning of life is one of the most profound and debated questions in philosophy, religion, and science. Different perspectives offer various answers:\\n\\n1. **Philosophical Perspectives:** \\n - **Existentialism (e.g., Sartre, Camus):** Life has no inherent meaning—we must create our own purpose through choices and actions. \\n - **Absurdism (Camus):** The search for meaning in a meaningless universe is absurd, but we must embrace life anyway. \\n - **Stoicism:** Meaning comes from virtue, wisdom, and living in harmony with nature. \\n\\n2. **Religious/Spiritual Views:** \\n - **Theistic (Christianity, Islam, etc.):** Life’s purpose is to serve, love, or unite with God (or the divine). \\n - **Eastern Traditions (Buddhism, Hinduism):** Meaning may involve enlightenment (breaking the cycle of suffering) or fulfilling dharma (duty). \\n\\n3. **Scientific Perspectives:** \\n - **Biological:** Life’s \"purpose\" is survival, reproduction, and passing on genes (evolutionary biology). \\n - **Cosmic:** From a physics standpoint, life may be a rare, fleeting phenomenon in an indifferent universe. \\n\\n4. **Personal/Subjective Meaning:** \\n Many find purpose in relationships, creativity, helping others, or personal growth. Viktor Frankl (Holocaust survivor) argued that meaning arises from suffering, love, and work. \\n\\n**Short answer?** There’s no universal meaning—it’s up to you to define it. What gives *your* life purpose? \\n\\nWould you like to explore a specific perspective?', finish_reason=None)\n"
1428
+ ]
1429
+ }
1430
+ ],
1431
+ "source": [
1432
+ "from pprint import pprint\n",
1433
+ "pprint(completion)"
1434
+ ]
1435
+ },
1436
+ {
1437
+ "cell_type": "code",
1438
+ "execution_count": 8,
1439
+ "id": "ff185a0a",
1440
+ "metadata": {},
1441
+ "outputs": [
1442
+ {
1443
+ "name": "stdout",
1444
+ "output_type": "stream",
1445
+ "text": [
1446
+ "Note: you may need to restart the kernel to use updated packages.Collecting indoxrouter\n",
1447
+ " Using cached indoxrouter-0.1.15-py3-none-any.whl.metadata (5.4 kB)\n",
1448
+ "Requirement already satisfied: requests>=2.25.0 in e:\\anaconda\\lib\\site-packages (from indoxrouter) (2.32.3)\n",
1449
+ "Requirement already satisfied: python-dotenv>=1.0.0 in e:\\anaconda\\lib\\site-packages (from indoxrouter) (1.0.1)\n",
1450
+ "Requirement already satisfied: charset-normalizer<4,>=2 in e:\\anaconda\\lib\\site-packages (from requests>=2.25.0->indoxrouter) (3.3.2)\n",
1451
+ "Requirement already satisfied: idna<4,>=2.5 in e:\\anaconda\\lib\\site-packages (from requests>=2.25.0->indoxrouter) (3.7)\n",
1452
+ "Requirement already satisfied: urllib3<3,>=1.21.1 in e:\\anaconda\\lib\\site-packages (from requests>=2.25.0->indoxrouter) (2.2.2)\n",
1453
+ "Requirement already satisfied: certifi>=2017.4.17 in e:\\anaconda\\lib\\site-packages (from requests>=2.25.0->indoxrouter) (2024.6.2)\n",
1454
+ "Using cached indoxrouter-0.1.15-py3-none-any.whl (11 kB)\n",
1455
+ "Installing collected packages: indoxrouter\n",
1456
+ "Successfully installed indoxrouter-0.1.15\n",
1457
+ "\n"
1458
+ ]
1459
+ },
1460
+ {
1461
+ "name": "stderr",
1462
+ "output_type": "stream",
1463
+ "text": [
1464
+ "\n",
1465
+ "[notice] A new release of pip is available: 24.3.1 -> 25.1.1\n",
1466
+ "[notice] To update, run: python.exe -m pip install --upgrade pip\n"
1467
+ ]
1468
+ }
1469
+ ],
1470
+ "source": [
1471
+ "pip install indoxrouter"
1472
+ ]
1473
+ },
1474
+ {
1475
+ "cell_type": "code",
1476
+ "execution_count": 9,
1477
+ "id": "b584d7c4",
1478
+ "metadata": {},
1479
+ "outputs": [
1480
+ {
1481
+ "data": {
1482
+ "text/plain": [
1483
+ "{'request_id': 'c08cc108-6b0d-48bd-a660-546143f1b9fa',\n",
1484
+ " 'created_at': '2025-05-19T06:07:38.077269',\n",
1485
+ " 'duration_ms': 9664.651870727539,\n",
1486
+ " 'provider': 'deepseek',\n",
1487
+ " 'model': 'deepseek-chat',\n",
1488
+ " 'success': True,\n",
1489
+ " 'message': '',\n",
1490
+ " 'usage': {'tokens_prompt': 15,\n",
1491
+ " 'tokens_completion': 107,\n",
1492
+ " 'tokens_total': 122,\n",
1493
+ " 'cost': 0.000229,\n",
1494
+ " 'latency': 9.487398862838745,\n",
1495
+ " 'timestamp': '2025-05-19T06:07:38.065330'},\n",
1496
+ " 'raw_response': None,\n",
1497
+ " 'data': 'In a bustling city, a small cleaning robot named Pip discovered a lost kitten in an alley. Despite its simple programming, Pip felt a strange urge to protect the tiny creature. It carried the kitten through busy streets, dodging humans and traffic, until it found a kind woman who gasped in delight. The woman took the kitten home, and Pip watched from the sidewalk, its circuits humming with something like happiness. That night, as it returned to its charging station, Pip replayed the memory—its first act of kindness beyond code.',\n",
1498
+ " 'finish_reason': None}"
1499
+ ]
1500
+ },
1501
+ "execution_count": 9,
1502
+ "metadata": {},
1503
+ "output_type": "execute_result"
1504
+ }
1505
+ ],
1506
+ "source": [
1507
+ "from indoxrouter import Client\n",
1508
+ "client= Client(api_key=\"indox-vgfI5PEfUoZ6qufR2z6QqXFV4BHgxrez\")\n",
1509
+ "client.chat(\n",
1510
+ " messages=[\n",
1511
+ " {\"role\": \"user\", \"content\": \"Tell me a story about a robot in 5 sentences.\"}\n",
1512
+ " ],\n",
1513
+ " model=\"deepseek/deepseek-chat\",\n",
1514
+ ")"
1515
+ ]
1361
1516
  }
1362
1517
  ],
1363
1518
  "metadata": {
@@ -30,7 +30,7 @@ For custom server URLs:
30
30
  ```
31
31
  """
32
32
 
33
- from .client import Client
33
+ from .client import Client, IndoxRouter
34
34
  from .exceptions import (
35
35
  IndoxRouterError,
36
36
  AuthenticationError,
@@ -39,13 +39,18 @@ from .exceptions import (
39
39
  ProviderError,
40
40
  ModelNotFoundError,
41
41
  ProviderNotFoundError,
42
+ ModelNotAvailableError,
42
43
  InvalidParametersError,
44
+ RequestError,
43
45
  InsufficientCreditsError,
46
+ ValidationError,
47
+ APIError,
44
48
  )
45
49
 
46
50
  __version__ = "0.2.1"
47
51
  __all__ = [
48
52
  "Client",
53
+ "IndoxRouter",
49
54
  "IndoxRouterError",
50
55
  "AuthenticationError",
51
56
  "NetworkError",
@@ -53,6 +58,10 @@ __all__ = [
53
58
  "ProviderError",
54
59
  "ModelNotFoundError",
55
60
  "ProviderNotFoundError",
61
+ "ModelNotAvailableError",
56
62
  "InvalidParametersError",
63
+ "RequestError",
57
64
  "InsufficientCreditsError",
65
+ "ValidationError",
66
+ "APIError",
58
67
  ]
@@ -57,10 +57,14 @@ from .exceptions import (
57
57
  NetworkError,
58
58
  ProviderNotFoundError,
59
59
  ModelNotFoundError,
60
+ ModelNotAvailableError,
60
61
  InvalidParametersError,
61
62
  RateLimitError,
62
63
  ProviderError,
64
+ RequestError,
63
65
  InsufficientCreditsError,
66
+ ValidationError,
67
+ APIError,
64
68
  )
65
69
  from .constants import (
66
70
  DEFAULT_BASE_URL,
@@ -316,29 +320,57 @@ class Client:
316
320
  if "provider" in error_message.lower():
317
321
  raise ProviderNotFoundError(error_message)
318
322
  elif "model" in error_message.lower():
319
- raise ModelNotFoundError(error_message)
323
+ # Check if it's a model not found vs model not available
324
+ if (
325
+ "not supported" in error_message.lower()
326
+ or "disabled" in error_message.lower()
327
+ or "unavailable" in error_message.lower()
328
+ ):
329
+ raise ModelNotAvailableError(error_message)
330
+ else:
331
+ raise ModelNotFoundError(error_message)
320
332
  else:
321
- raise NetworkError(
322
- f"Resource not found: {error_message} (URL: {url})"
323
- )
333
+ raise APIError(f"Resource not found: {error_message} (URL: {url})")
324
334
  elif status_code == 429:
325
335
  raise RateLimitError(f"Rate limit exceeded: {error_message}")
326
336
  elif status_code == 400:
327
- raise InvalidParametersError(f"Invalid parameters: {error_message}")
337
+ # Check if it's a validation error or invalid parameters
338
+ if (
339
+ "validation" in error_message.lower()
340
+ or "invalid format" in error_message.lower()
341
+ ):
342
+ raise ValidationError(f"Request validation failed: {error_message}")
343
+ else:
344
+ raise InvalidParametersError(f"Invalid parameters: {error_message}")
328
345
  elif status_code == 402:
329
346
  raise InsufficientCreditsError(f"Insufficient credits: {error_message}")
347
+ elif status_code == 422:
348
+ # Unprocessable Entity - typically validation errors
349
+ raise ValidationError(f"Request validation failed: {error_message}")
350
+ elif status_code == 503:
351
+ # Service Unavailable - model might be temporarily unavailable
352
+ if "model" in error_message.lower():
353
+ raise ModelNotAvailableError(
354
+ f"Model temporarily unavailable: {error_message}"
355
+ )
356
+ else:
357
+ raise APIError(f"Service unavailable: {error_message}")
330
358
  elif status_code == 500:
331
359
  # Provide more detailed information for server errors
332
360
  error_detail = error_data.get("detail", "No details provided")
333
361
  # Include the request data in the error message for better debugging
334
362
  request_data_str = json.dumps(data, indent=2) if data else "None"
335
- raise ProviderError(
363
+ raise RequestError(
336
364
  f"Server error (500): {error_detail}. URL: {url}.\n"
337
365
  f"Request data: {request_data_str}\n"
338
366
  f"This may indicate an issue with the server configuration or a problem with the provider service."
339
367
  )
368
+ elif status_code >= 400 and status_code < 500:
369
+ # Client errors
370
+ raise APIError(f"Client error ({status_code}): {error_message}")
340
371
  else:
341
- raise ProviderError(f"Provider error ({status_code}): {error_message}")
372
+ # Server errors
373
+ raise RequestError(f"Server error ({status_code}): {error_message}")
342
374
  except requests.RequestException as e:
343
375
  logger.error(f"Request exception: {str(e)}")
344
376
  raise NetworkError(f"Network error: {str(e)}")
@@ -4,7 +4,7 @@ Constants for the IndoxRouter client.
4
4
 
5
5
  # API settings
6
6
  DEFAULT_API_VERSION = "v1"
7
- DEFAULT_BASE_URL = "https://api.indox.org" # Production server URL with HTTPS
7
+ DEFAULT_BASE_URL = "https://api.indoxrouter.com" # Production server URL with HTTPS
8
8
  # DEFAULT_BASE_URL = "http://localhost:8000" # Local development server
9
9
  DEFAULT_TIMEOUT = 60
10
10
  USE_COOKIES = True # Always use cookie-based authentication
@@ -38,14 +38,20 @@ class ProviderError(IndoxRouterError):
38
38
  pass
39
39
 
40
40
 
41
+ class ProviderNotFoundError(ProviderError):
42
+ """Raised when a requested provider is not found."""
43
+
44
+ pass
45
+
46
+
41
47
  class ModelNotFoundError(ProviderError):
42
48
  """Raised when a requested model is not found."""
43
49
 
44
50
  pass
45
51
 
46
52
 
47
- class ProviderNotFoundError(ProviderError):
48
- """Raised when a requested provider is not found."""
53
+ class ModelNotAvailableError(ProviderError):
54
+ """Raised when a model is disabled or not supported by the provider."""
49
55
 
50
56
  pass
51
57
 
@@ -56,7 +62,25 @@ class InvalidParametersError(IndoxRouterError):
56
62
  pass
57
63
 
58
64
 
65
+ class RequestError(IndoxRouterError):
66
+ """Raised when a request to a provider fails."""
67
+
68
+ pass
69
+
70
+
59
71
  class InsufficientCreditsError(IndoxRouterError):
60
72
  """Raised when the user doesn't have enough credits."""
61
73
 
62
74
  pass
75
+
76
+
77
+ class ValidationError(IndoxRouterError):
78
+ """Raised when request validation fails."""
79
+
80
+ pass
81
+
82
+
83
+ class APIError(IndoxRouterError):
84
+ """Raised when the API returns an error."""
85
+
86
+ pass
@@ -1,10 +1,13 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: indoxrouter
3
- Version: 0.1.12
3
+ Version: 0.1.16
4
4
  Summary: A unified client for various AI providers
5
- Home-page: https://github.com/indoxrouter/indoxrouter
6
- Author: indoxRouter Team
7
- Author-email: ashkan.eskandari.dev@gmail.com
5
+ Author-email: indoxRouter Team <ashkan.eskandari.dev@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/indoxrouter/indoxrouter
8
+ Project-URL: Repository, https://github.com/indoxrouter/indoxrouter
9
+ Project-URL: Issues, https://github.com/indoxrouter/indoxrouter/issues
10
+ Keywords: ai,api,client,openai,anthropic,google,mistral
8
11
  Classifier: Development Status :: 3 - Alpha
9
12
  Classifier: Intended Audience :: Developers
10
13
  Classifier: License :: OSI Approved :: MIT License
@@ -18,27 +21,10 @@ Requires-Python: >=3.8
18
21
  Description-Content-Type: text/markdown
19
22
  Requires-Dist: requests>=2.25.0
20
23
  Requires-Dist: python-dotenv>=1.0.0
21
- Provides-Extra: dev
22
- Requires-Dist: pytest>=7.0.0; extra == "dev"
23
- Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
24
- Requires-Dist: black>=23.0.0; extra == "dev"
25
- Requires-Dist: isort>=5.0.0; extra == "dev"
26
- Requires-Dist: flake8>=6.0.0; extra == "dev"
27
- Requires-Dist: mypy>=1.0.0; extra == "dev"
28
- Dynamic: author
29
- Dynamic: author-email
30
- Dynamic: classifier
31
- Dynamic: description
32
- Dynamic: description-content-type
33
- Dynamic: home-page
34
- Dynamic: provides-extra
35
- Dynamic: requires-dist
36
- Dynamic: requires-python
37
- Dynamic: summary
38
24
 
39
25
  # IndoxRouter Client
40
26
 
41
- A unified client for various AI providers, including OpenAI, Anthropic, Google, and Mistral.
27
+ A unified client for various AI providers, including OpenAI, anthropic, Google, and Mistral.
42
28
 
43
29
  ## Features
44
30
 
@@ -1,6 +1,6 @@
1
1
  MANIFEST.in
2
2
  README.md
3
- setup.py
3
+ pyproject.toml
4
4
  cookbook/README.md
5
5
  cookbook/indoxRouter_cookbook.ipynb
6
6
  indoxrouter/__init__.py
@@ -0,0 +1,2 @@
1
+ requests>=2.25.0
2
+ python-dotenv>=1.0.0
@@ -0,0 +1,41 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "indoxrouter"
7
+ version = "0.1.16"
8
+ authors = [
9
+ {name = "indoxRouter Team", email = "ashkan.eskandari.dev@gmail.com"},
10
+ ]
11
+ description = "A unified client for various AI providers"
12
+ readme = "README.md"
13
+ requires-python = ">=3.8"
14
+ license = {text = "MIT"}
15
+ keywords = ["ai", "api", "client", "openai", "anthropic", "google", "mistral"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.8",
22
+ "Programming Language :: Python :: 3.9",
23
+ "Programming Language :: Python :: 3.10",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ ]
27
+ dependencies = [
28
+ "requests>=2.25.0",
29
+ "python-dotenv>=1.0.0",
30
+ ]
31
+
32
+ [project.urls]
33
+ Homepage = "https://github.com/indoxrouter/indoxrouter"
34
+ Repository = "https://github.com/indoxrouter/indoxrouter"
35
+ Issues = "https://github.com/indoxrouter/indoxrouter/issues"
36
+
37
+ [tool.setuptools.packages.find]
38
+ where = ["."]
39
+ include = ["indoxrouter*"]
40
+ exclude = ["tests*"]
41
+
@@ -1,10 +0,0 @@
1
- requests>=2.25.0
2
- python-dotenv>=1.0.0
3
-
4
- [dev]
5
- pytest>=7.0.0
6
- pytest-cov>=4.0.0
7
- black>=23.0.0
8
- isort>=5.0.0
9
- flake8>=6.0.0
10
- mypy>=1.0.0
@@ -1,46 +0,0 @@
1
- """
2
- Setup script for indoxRouter client.
3
- """
4
-
5
- from setuptools import setup, find_packages
6
-
7
- with open("README.md", "r", encoding="utf-8") as fh:
8
- long_description = fh.read()
9
-
10
- setup(
11
- name="indoxrouter",
12
- version="0.1.12",
13
- author="indoxRouter Team",
14
- author_email="ashkan.eskandari.dev@gmail.com",
15
- description="A unified client for various AI providers",
16
- long_description=long_description,
17
- long_description_content_type="text/markdown",
18
- url="https://github.com/indoxrouter/indoxrouter",
19
- packages=["indoxrouter"],
20
- classifiers=[
21
- "Development Status :: 3 - Alpha",
22
- "Intended Audience :: Developers",
23
- "License :: OSI Approved :: MIT License",
24
- "Programming Language :: Python :: 3",
25
- "Programming Language :: Python :: 3.8",
26
- "Programming Language :: Python :: 3.9",
27
- "Programming Language :: Python :: 3.10",
28
- "Programming Language :: Python :: 3.11",
29
- "Programming Language :: Python :: 3.12",
30
- ],
31
- python_requires=">=3.8",
32
- install_requires=[
33
- "requests>=2.25.0",
34
- "python-dotenv>=1.0.0",
35
- ],
36
- extras_require={
37
- "dev": [
38
- "pytest>=7.0.0",
39
- "pytest-cov>=4.0.0",
40
- "black>=23.0.0",
41
- "isort>=5.0.0",
42
- "flake8>=6.0.0",
43
- "mypy>=1.0.0",
44
- ],
45
- },
46
- )
File without changes
File without changes