davidkhala.ai 0.2.1__py3-none-any.whl → 0.2.2__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.
Files changed (52) hide show
  1. davidkhala/ai/agent/dify/api/__init__.py +2 -2
  2. davidkhala/ai/agent/dify/api/app.py +10 -6
  3. davidkhala/ai/agent/dify/api/knowledge/__init__.py +0 -0
  4. davidkhala/ai/agent/dify/api/knowledge/chunk.py +14 -0
  5. davidkhala/ai/agent/dify/api/knowledge/dataset.py +82 -0
  6. davidkhala/ai/agent/dify/api/knowledge/document.py +42 -0
  7. davidkhala/ai/agent/dify/api/knowledge/model.py +139 -0
  8. davidkhala/ai/agent/dify/{ops/console → console}/__init__.py +7 -1
  9. davidkhala/ai/agent/dify/console/knowledge/__init__.py +0 -0
  10. davidkhala/ai/agent/dify/console/knowledge/dataset.py +61 -0
  11. davidkhala/ai/agent/dify/console/knowledge/pipeline.py +127 -0
  12. davidkhala/ai/agent/dify/{ops/console → console}/plugin.py +20 -6
  13. davidkhala/ai/agent/dify/console/session.py +50 -0
  14. davidkhala/ai/agent/dify/db/orm.py +65 -0
  15. davidkhala/ai/agent/dify/model/__init__.py +7 -0
  16. davidkhala/ai/agent/dify/{model.py → model/knowledge.py} +1 -12
  17. davidkhala/ai/agent/dify/{ops/db/orm.py → model/workflow.py} +24 -62
  18. davidkhala/ai/agent/langgraph.py +1 -1
  19. davidkhala/ai/ali/dashscope.py +15 -14
  20. davidkhala/ai/anthropic/__init__.py +6 -0
  21. davidkhala/ai/api/__init__.py +6 -19
  22. davidkhala/ai/api/openrouter.py +14 -10
  23. davidkhala/ai/api/siliconflow.py +2 -4
  24. davidkhala/ai/atlas/__init__.py +24 -0
  25. davidkhala/ai/mistral/__init__.py +2 -20
  26. davidkhala/ai/mistral/agent.py +50 -0
  27. davidkhala/ai/mistral/ai.py +40 -0
  28. davidkhala/ai/mistral/file.py +38 -0
  29. davidkhala/ai/mistral/ocr.py +46 -0
  30. davidkhala/ai/model/__init__.py +11 -27
  31. davidkhala/ai/model/chat.py +60 -4
  32. davidkhala/ai/model/embed.py +8 -0
  33. davidkhala/ai/model/garden.py +9 -0
  34. davidkhala/ai/openai/__init__.py +9 -33
  35. davidkhala/ai/openai/azure.py +51 -0
  36. davidkhala/ai/openai/native.py +2 -3
  37. davidkhala/ai/openrouter/__init__.py +24 -13
  38. {davidkhala_ai-0.2.1.dist-info → davidkhala_ai-0.2.2.dist-info}/METADATA +8 -6
  39. davidkhala_ai-0.2.2.dist-info/RECORD +65 -0
  40. davidkhala/ai/agent/dify/api/knowledge.py +0 -191
  41. davidkhala/ai/agent/dify/ops/__init__.py +0 -1
  42. davidkhala/ai/agent/dify/ops/console/knowledge.py +0 -158
  43. davidkhala/ai/agent/dify/ops/console/session.py +0 -32
  44. davidkhala/ai/huggingface/BAAI.py +0 -10
  45. davidkhala/ai/huggingface/__init__.py +0 -21
  46. davidkhala/ai/huggingface/inference.py +0 -13
  47. davidkhala_ai-0.2.1.dist-info/RECORD +0 -53
  48. /davidkhala/ai/agent/dify/{ops/db → db}/__init__.py +0 -0
  49. /davidkhala/ai/agent/dify/{ops/db → db}/app.py +0 -0
  50. /davidkhala/ai/agent/dify/{ops/db → db}/knowledge.py +0 -0
  51. /davidkhala/ai/agent/dify/{ops/db → db}/sys.py +0 -0
  52. {davidkhala_ai-0.2.1.dist-info → davidkhala_ai-0.2.2.dist-info}/WHEEL +0 -0
@@ -1,4 +1,6 @@
1
- from typing import Protocol, Any
1
+ from typing import Protocol, Any, Iterable, TypedDict
2
+
3
+ from davidkhala.ai.model import ModelAware
2
4
 
3
5
 
4
6
  class MessageProtocol(Protocol):
@@ -13,7 +15,61 @@ class ChoicesAware(Protocol):
13
15
  choices: list[ChoiceProtocol]
14
16
 
15
17
 
16
- def on_response(response: ChoicesAware, n: int):
18
+ class ImagePromptDict(TypedDict):
19
+ text: str
20
+ image_url: list[str]
21
+
22
+
23
+ def on_response(response: ChoicesAware, n: int | None):
17
24
  contents = [choice.message.content for choice in response.choices]
18
- assert len(contents) == n
19
- return contents
25
+ if n:
26
+ assert len(contents) == n, f"expected {n} choices, but got {len(contents)}"
27
+ return contents
28
+
29
+
30
+ class MessageDict(TypedDict):
31
+ content: str | list | None
32
+ role: str
33
+
34
+
35
+ def messages_from(*user_prompt: str | ImagePromptDict) -> Iterable[MessageDict]:
36
+ for _ in user_prompt:
37
+ message = MessageDict(role='user', content=None)
38
+ if type(_) == str:
39
+ message['content'] = _
40
+ elif type(_) == dict:
41
+ message['content'] = [{"type": "text", "text": _['text']}]
42
+ message['content'].extend({"type": "image_url", "image_url": {"url": i}} for i in _['image_url'])
43
+ yield message
44
+
45
+
46
+ class ChatAware(ModelAware):
47
+ def __init__(self):
48
+ super().__init__()
49
+ self.messages: list[Any | MessageDict] = []
50
+ self.n: int = 1
51
+
52
+ def as_chat(self, model: str | None, sys_prompt: str = None):
53
+ self.model = model
54
+ if sys_prompt is not None:
55
+ self.messages = [MessageDict(role='system', content=sys_prompt)]
56
+
57
+ def chat(self, *user_prompt, **kwargs): ...
58
+
59
+ def messages_from(self, *user_prompt) -> list[MessageDict]:
60
+ messages = list(self.messages)
61
+ messages.extend(messages_from(*user_prompt))
62
+ return messages
63
+
64
+
65
+ class CompareChatAware(ChatAware):
66
+ def __init__(self):
67
+ super().__init__()
68
+ self._models = None
69
+
70
+ def as_chat(self, *models: str, sys_prompt: str = None):
71
+ if len(models) > 1:
72
+ self._models = models
73
+ super().as_chat(None, sys_prompt)
74
+ elif len(models) == 1:
75
+ super().as_chat(models[0], sys_prompt)
@@ -0,0 +1,8 @@
1
+ from davidkhala.ai.model import ModelAware
2
+
3
+
4
+ class EmbeddingAware(ModelAware):
5
+ def as_embeddings(self, model: str):
6
+ self.model = model
7
+
8
+ def encode(self, *_input: str) -> list[list[float]]: ...
@@ -0,0 +1,9 @@
1
+ from typing import Protocol
2
+
3
+
4
+ class GardenAlike(Protocol):
5
+ @property
6
+ def models(self) -> list[str]: ...
7
+
8
+ @property
9
+ def free_models(self) -> list[str]: ...
@@ -1,20 +1,19 @@
1
1
  from typing import Literal
2
2
 
3
- from httpx import URL
4
3
  from openai import OpenAI
5
4
 
6
- from davidkhala.ai.model import AbstractClient
7
- from davidkhala.ai.model.chat import on_response
5
+ from davidkhala.ai.model import SDKProtocol, Connectable
6
+ from davidkhala.ai.model.embed import EmbeddingAware
7
+ from davidkhala.ai.model.chat import on_response, ChatAware
8
+ from davidkhala.ai.model.garden import GardenAlike
8
9
 
9
10
 
10
- class Client(AbstractClient):
11
+ class Client(ChatAware, EmbeddingAware, SDKProtocol, GardenAlike, Connectable):
11
12
  def __init__(self, client: OpenAI):
12
13
  super().__init__()
13
- self.client:OpenAI = client
14
- self.base_url:URL = client.base_url
15
- self.api_key = client.api_key
16
- self.encoding_format:Literal["float", "base64"] = "float"
17
- self.n:int = 1
14
+ self.client: OpenAI = client
15
+ self.encoding_format: Literal["float", "base64"] = "float"
16
+
18
17
  def connect(self):
19
18
  try:
20
19
  type(self).models.fget(self)
@@ -36,29 +35,9 @@ class Client(AbstractClient):
36
35
 
37
36
  def chat(self, *user_prompt, **kwargs):
38
37
 
39
- messages = [
40
- *self.messages,
41
- ]
42
- for prompt in user_prompt:
43
- message = {
44
- "role": "user"
45
- }
46
- if type(prompt) == str:
47
- message['content'] = prompt
48
- elif type(prompt) == dict:
49
- message['content'] = [
50
- {"type": "text", "text": prompt['text']},
51
- {
52
- "type": "image_url",
53
- "image_url": {
54
- "url": prompt['image_url'],
55
- }
56
- },
57
- ]
58
- messages.append(message)
59
38
  response = self.client.chat.completions.create(
60
39
  model=self.model,
61
- messages=messages,
40
+ messages=self.messages_from(*user_prompt),
62
41
  n=self.n,
63
42
  **kwargs
64
43
  )
@@ -67,6 +46,3 @@ class Client(AbstractClient):
67
46
 
68
47
  def close(self):
69
48
  self.client.close()
70
-
71
-
72
-
@@ -1,6 +1,18 @@
1
+ import base64
2
+ import json
3
+ from pathlib import Path
4
+
5
+ from davidkhala.ml.ocr.interface import FieldProperties
1
6
  from davidkhala.utils.syntax.compat import deprecated
2
7
  from openai import AzureOpenAI, OpenAI
8
+ from openai.types.chat import (
9
+ ChatCompletionUserMessageParam, ChatCompletionContentPartTextParam, ChatCompletionContentPartImageParam
10
+ )
11
+ from openai.types.chat.chat_completion_content_part_image_param import ImageURL
12
+ from openai.types.shared_params import ResponseFormatJSONSchema
13
+ from openai.types.shared_params.response_format_json_schema import JSONSchema
3
14
 
15
+ from davidkhala.ai.model.chat import on_response
4
16
  from davidkhala.ai.openai import Client
5
17
 
6
18
 
@@ -19,6 +31,45 @@ class ModelDeploymentClient(AzureHosted):
19
31
  api_key=key,
20
32
  ))
21
33
 
34
+ def process(self, file: Path, schema: dict[str, FieldProperties])-> list[dict]:
35
+ with open(file, "rb") as f:
36
+ content = base64.b64encode(f.read()).decode("utf-8")
37
+ required = [k for k, _ in schema.items() if _.required]
38
+ properties = {k: {'type': v.type} for k, v in schema.items()}
39
+
40
+ json_schema = JSONSchema(
41
+ name='-',
42
+ schema={"type": "object",
43
+ "properties": properties,
44
+ "required": required,
45
+ },
46
+ )
47
+
48
+ self.messages.append(ChatCompletionUserMessageParam(
49
+ role='user',
50
+ content=[
51
+ ChatCompletionContentPartTextParam(
52
+ type='text',
53
+ text="Extract the required fields from this image and return the output strictly following the provided JSON schema."),
54
+ ChatCompletionContentPartImageParam(
55
+ type="image_url",
56
+ image_url=ImageURL(
57
+ url=f"data:image/jpeg;base64,{content}",
58
+ detail='auto'
59
+ )
60
+ )
61
+ ]
62
+ ))
63
+ response = self.client.chat.completions.create(
64
+ model=self.model,
65
+ messages=self.messages,
66
+ response_format=ResponseFormatJSONSchema(
67
+ type='json_schema',
68
+ json_schema=json_schema
69
+ ),
70
+ n=self.n,
71
+ )
72
+ return [json.loads(_) for _ in on_response(response, self.n)]
22
73
 
23
74
  @deprecated("Azure Open AI is deprecated. Please migrate to Microsoft Foundry")
24
75
  class OpenAIClient(AzureHosted):
@@ -1,4 +1,4 @@
1
- from typing import Optional, Literal
1
+ from typing import Literal
2
2
 
3
3
  from openai import OpenAI
4
4
 
@@ -12,8 +12,7 @@ class NativeClient(Client):
12
12
  base_url=base_url
13
13
  ))
14
14
 
15
-
16
- def chat(self, *user_prompt, web_search:Optional[Literal["low", "medium", "high"]]=None, **kwargs):
15
+ def chat(self, *user_prompt, web_search: Literal["low", "medium", "high"] = None, **kwargs):
17
16
  opts = {
18
17
  **kwargs
19
18
  }
@@ -1,36 +1,47 @@
1
+ from openrouter import OpenRouter
2
+ from openrouter.components import Model
1
3
  from openrouter.errors import UnauthorizedResponseError
4
+ from openrouter.operations import ListData
2
5
 
3
- from davidkhala.ai.model import AbstractClient
4
- from openrouter import OpenRouter
6
+ from davidkhala.ai.model import Connectable, SDKProtocol
7
+ from davidkhala.ai.model.chat import CompareChatAware, on_response
8
+ from davidkhala.ai.model.garden import GardenAlike
5
9
 
6
10
 
7
- class Client(AbstractClient):
11
+ class Client(CompareChatAware, Connectable, SDKProtocol, GardenAlike):
8
12
  def __init__(self, api_key: str):
9
13
  super().__init__()
10
- self.api_key = api_key
11
- self.client = OpenRouter(api_key)
14
+ self.client: OpenRouter = OpenRouter(api_key)
12
15
 
13
16
  def chat(self, *user_prompt, **kwargs):
14
17
  r = self.client.chat.send(
15
18
  model=self.model,
16
- messages=[
17
- *self.messages,
18
- *[{'role': 'user', 'content': _} for _ in user_prompt]
19
- ]
19
+ models=self._models,
20
+ messages=self.messages_from(*user_prompt)
21
+ # openrouter has no n
20
22
  )
21
- return [_.message.content for _ in r.choices]
23
+ return on_response(r, None)
24
+
22
25
  def connect(self):
23
26
  try:
24
- self.client.models.list()
27
+ _ = self.models
25
28
  return True
26
29
  except UnauthorizedResponseError:
27
30
  return False
28
31
 
32
+ def list_models(self) -> list[Model]:
33
+ return self.client.models.list().data
34
+
35
+ @property
36
+ def models(self) -> list[str]:
37
+ return [m.id for m in self.list_models()]
38
+
29
39
 
30
40
  class Admin:
31
41
  def __init__(self, provisioning_key: str):
32
42
  self.provisioning_key = provisioning_key
33
43
  self.client = OpenRouter(provisioning_key)
44
+
34
45
  @property
35
- def keys(self):
36
- return self.client.api_keys.list().data
46
+ def keys(self) -> list[ListData]:
47
+ return self.client.api_keys.list().data
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: davidkhala.ai
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: misc AI modules
5
5
  Requires-Python: >=3.12
6
6
  Provides-Extra: ali
@@ -9,7 +9,10 @@ Requires-Dist: davidkhala-utils; extra == 'ali'
9
9
  Requires-Dist: wuying-agentbay-sdk; extra == 'ali'
10
10
  Provides-Extra: api
11
11
  Requires-Dist: davidkhala-utils[http-request]; extra == 'api'
12
+ Provides-Extra: atlas
13
+ Requires-Dist: voyageai; extra == 'atlas'
12
14
  Provides-Extra: azure
15
+ Requires-Dist: davidkhala-ml-ocr; extra == 'azure'
13
16
  Requires-Dist: davidkhala-utils; extra == 'azure'
14
17
  Requires-Dist: openai; extra == 'azure'
15
18
  Provides-Extra: dify
@@ -19,16 +22,15 @@ Requires-Dist: dify-plugin; extra == 'dify'
19
22
  Provides-Extra: google
20
23
  Requires-Dist: google-adk; extra == 'google'
21
24
  Requires-Dist: google-genai; extra == 'google'
22
- Provides-Extra: hf
23
- Requires-Dist: hf-xet; extra == 'hf'
24
- Requires-Dist: huggingface-hub; extra == 'hf'
25
- Requires-Dist: onnx; extra == 'hf'
26
- Requires-Dist: onnxruntime; extra == 'hf'
27
25
  Provides-Extra: langchain
28
26
  Requires-Dist: langchain; extra == 'langchain'
29
27
  Requires-Dist: langchain-openai; (python_version < '3.14') and extra == 'langchain'
30
28
  Requires-Dist: langgraph; extra == 'langchain'
29
+ Provides-Extra: minimax
30
+ Requires-Dist: anthropic; extra == 'minimax'
31
+ Requires-Dist: openai; extra == 'minimax'
31
32
  Provides-Extra: mistral
33
+ Requires-Dist: davidkhala-ml-ocr; extra == 'mistral'
32
34
  Requires-Dist: mistralai; extra == 'mistral'
33
35
  Provides-Extra: openrouter
34
36
  Requires-Dist: openrouter; extra == 'openrouter'
@@ -0,0 +1,65 @@
1
+ davidkhala/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ davidkhala/ai/opik.py,sha256=YU1XuweMUAzUkhpjxhltt-SBBDBkR3z-PCNo0DqzBRs,39
3
+ davidkhala/ai/you.py,sha256=fmMfJQZcG0e6sKfVQy2jOKZ-jkO-3H6GOGP2U19l4zQ,1759
4
+ davidkhala/ai/agent/README.md,sha256=kIPsx3gOjrpOw7w2qhNEALuCEQkuh4nYp6uBnijdvHE,178
5
+ davidkhala/ai/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ davidkhala/ai/agent/langgraph.py,sha256=vaiHpnYmkjfXiGq-VH0sUn-NXBacnbFW-6E6sUgdNLU,1050
7
+ davidkhala/ai/agent/ragflow.py,sha256=UaK31us6V0NhAPCthGo07rQsm72vlR-McmihC_NDe1g,273
8
+ davidkhala/ai/agent/dify/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ davidkhala/ai/agent/dify/const.py,sha256=gU4lPBe4U2taakN2jhdPMRWXkqlyCg-YRE8JJmtsblo,218
10
+ davidkhala/ai/agent/dify/interface.py,sha256=bTOI38ZjtkgoSw-ysgFwBZ1QkKVAa92gjOnERDoagQA,118
11
+ davidkhala/ai/agent/dify/api/__init__.py,sha256=OevdexhW4rh40uGpzSxmeeff4pslKvygKpxjUQzMc-Y,861
12
+ davidkhala/ai/agent/dify/api/app.py,sha256=XMLwNSpL5BhNZHKx4BEuk9NplIHCToMKXFssrn_7Hu4,3931
13
+ davidkhala/ai/agent/dify/api/knowledge/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ davidkhala/ai/agent/dify/api/knowledge/chunk.py,sha256=AurKJCZQQ2RkitLxMRcRPRKOE2WsTm7JSb6AnjJEgEw,470
15
+ davidkhala/ai/agent/dify/api/knowledge/dataset.py,sha256=JO0Au4TTZIzSagkDI1lOULB5SM-WWGnIiQ7HmcZardM,2981
16
+ davidkhala/ai/agent/dify/api/knowledge/document.py,sha256=sjgzwLWar2CsqO8gF93qeRfzw3fNCLgC4PGxD7QhAhE,1261
17
+ davidkhala/ai/agent/dify/api/knowledge/model.py,sha256=fRv4qMsgPMbNU3Q9c03rU4JedoktQ38IjUDrAjQy-8w,3775
18
+ davidkhala/ai/agent/dify/console/__init__.py,sha256=WKXcjjtnWyljhlgrReSneeD87K6GmgGoCmwKCPdGPdc,380
19
+ davidkhala/ai/agent/dify/console/plugin.py,sha256=qwojlnYaIIRqjV8unN1mkMdOT5scG7ZhuVW8Rz8GgVM,2870
20
+ davidkhala/ai/agent/dify/console/session.py,sha256=agS6i7qe-ZGMOawNTh23_bY2ovm0JtGMDLwEuBuRzyk,1604
21
+ davidkhala/ai/agent/dify/console/knowledge/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ davidkhala/ai/agent/dify/console/knowledge/dataset.py,sha256=QgHPJSPQmiosc7WI6zLPBXaWLIEOHHOEZlVWNlPljuc,2474
23
+ davidkhala/ai/agent/dify/console/knowledge/pipeline.py,sha256=yg4pYx3PHKFl88_deexv6GgACyUcmnJXgvB91wXyfO4,4353
24
+ davidkhala/ai/agent/dify/db/__init__.py,sha256=HYfJEnoFAoJJck2xvTDYx8zpw9Qao7sHXOGvW0diPqw,517
25
+ davidkhala/ai/agent/dify/db/app.py,sha256=IRiSiR0v387p4p3J7M9xEkJ7pfQyO5DL6chpx7Z2IzA,1319
26
+ davidkhala/ai/agent/dify/db/knowledge.py,sha256=GVaK5QmU_VxB8fDxV60uiYiIeR3JEn3IXJTlJHLiT5U,2917
27
+ davidkhala/ai/agent/dify/db/orm.py,sha256=AJ2tVS18g-uyIPJGdEVIPAtoPYUm3KqzSl7av0S8uzA,2188
28
+ davidkhala/ai/agent/dify/db/sys.py,sha256=U_qqopUMlgsilhHaG_ids6gtd-pNiR_Jm0kAr9hIL7M,188
29
+ davidkhala/ai/agent/dify/model/__init__.py,sha256=VXKaNpQFK8J-j9zp5caOgiQxaQCIoUJjWn6NAjTTok0,104
30
+ davidkhala/ai/agent/dify/model/knowledge.py,sha256=e4CbI3tjIn0gsh4t4ZOMp-JQtWFB7yzWvkioSztWJx0,397
31
+ davidkhala/ai/agent/dify/model/workflow.py,sha256=I7_INuD5QAFT_xERmOwAMOKZEccGZ5COnvivv5fNisA,3044
32
+ davidkhala/ai/agent/dify/plugins/__init__.py,sha256=iTWvutlkN9bXgptesi05M447nTeF5hKFAIfn4EviFj0,183
33
+ davidkhala/ai/agent/dify/plugins/file.py,sha256=o-HjHSFwRTNIYs8IxqZUSnBbh-xr8f-xMUM3iU9wCCQ,390
34
+ davidkhala/ai/agent/dify/plugins/firecrawl.py,sha256=lB_f8W_bdg-7PeBKmF0-HdwYyakV_0D3nET5iT-Z1KM,460
35
+ davidkhala/ai/agent/dify/plugins/jina.py,sha256=dQ5iJxDLWtChXb1IjCtsHctgUtgjOiDfWOuR2u0aUIM,190
36
+ davidkhala/ai/agent/dify/plugins/popular.py,sha256=XMuxqRcIko4gCQORg5HCcwCrbgLcaoRZxCYBIgtkrOo,812
37
+ davidkhala/ai/ali/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
+ davidkhala/ai/ali/agentbay.py,sha256=O5t71GGwtDgBE1zUXJDYe5djMVwSaNOwn5k8zg1xa18,1200
39
+ davidkhala/ai/ali/dashscope.py,sha256=4KWxl8s6PrKtjgszYM6OIv6r5736EXC1HwLcfJo-x-8,2091
40
+ davidkhala/ai/anthropic/__init__.py,sha256=zzELo9KUD9wKKzrEPYmt-wBHPIvInHzgU2ClfbdgzYo,112
41
+ davidkhala/ai/api/__init__.py,sha256=FTsOpTnG5FxM80mFUYir2THfKmVQEGYnsoO_Egdypzo,1137
42
+ davidkhala/ai/api/openrouter.py,sha256=BBM0HsBwo29PChI4ca8tNiEbzE16-WWc95TqW6k-xn8,2527
43
+ davidkhala/ai/api/siliconflow.py,sha256=hxaEIQc2xi394SS8VdCXu2TWPxjpgc3hATzB03LsZQ0,2073
44
+ davidkhala/ai/atlas/__init__.py,sha256=hNjmpp9fk-GGXVZOjrvs_x4j0b_Wlew-BlC5RMTUEMA,746
45
+ davidkhala/ai/google/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
46
+ davidkhala/ai/google/adk.py,sha256=QwxYoOzT2Hol03V4NM0PF_HAzUGb4fB18VUAYacYbAY,657
47
+ davidkhala/ai/google/gemini.py,sha256=Xf4HDOOcK4-jEBERzuLnQNFsU61P2fFx4K0z-ijvNHE,214
48
+ davidkhala/ai/mistral/__init__.py,sha256=RpBOQFg67KFSuzaZ1LvuMtIpkfm-iTm6ZepkmHyggrg,371
49
+ davidkhala/ai/mistral/agent.py,sha256=hXHQj6MtxmR86Db4YbtMcdMi5NebX_Tg2bxYo_lL0sY,1652
50
+ davidkhala/ai/mistral/ai.py,sha256=SIIVjmR6oY4BjDTnYCiztkSJgfRF7_a3g21KoDQ0fVo,1324
51
+ davidkhala/ai/mistral/file.py,sha256=Y8RnaONasZX6zJDWzrG0Ez7epQ_BS3CKiIK8EqqX9YE,1402
52
+ davidkhala/ai/mistral/ocr.py,sha256=iImHiv784ZYdqKPwbUJhy7EL9WAXSpc4h4ixTZi3FHY,1736
53
+ davidkhala/ai/model/__init__.py,sha256=ODvM3qaEIjNJcJeU6Nkozj77LUWlC1HcPM-ifYVjA5s,490
54
+ davidkhala/ai/model/chat.py,sha256=1RgvdqMh7wK3bxiu5g3lDI89zVeuASSMKgeCJxqsiTI,2178
55
+ davidkhala/ai/model/embed.py,sha256=2W5Z-qKJUaROxuqV4yqqy7w666PDoh6RBj9gWFKWhUY,217
56
+ davidkhala/ai/model/garden.py,sha256=vcG6-fO0SJckNiPJ1wLJzsu6yKOIdQcVDARI9f9WQNg,178
57
+ davidkhala/ai/openai/__init__.py,sha256=-9SayyencR2nvGycYt_HF-8VqUXmQW9lIwYJV95RXCc,1443
58
+ davidkhala/ai/openai/azure.py,sha256=npF0GA-1jkKL6bsDyMrStkJODn1ShTsqdOKzsLu-mgc,3257
59
+ davidkhala/ai/openai/databricks.py,sha256=Z_ES3KlZuwMjMiGnlAxxrirLInQRVXj2KZs32MHXBbI,736
60
+ davidkhala/ai/openai/native.py,sha256=WhtpBCx_iGyy6tolGjJm1_gJtGo-dcmc_7QvnaxQ6WY,691
61
+ davidkhala/ai/openai/opik.py,sha256=ovodFNDoDO_ygX8_EmeqbZcu8PRTntzsNNQg4JoZKWo,257
62
+ davidkhala/ai/openrouter/__init__.py,sha256=8TLj1noeAvEzhowvkbLu2KUiR8KNijv8OMYRt6G7ApQ,1473
63
+ davidkhala_ai-0.2.2.dist-info/METADATA,sha256=pa6tpzTxuokcqYOsmvCx_oLdhS_JZ_QInQGC0PN7RY0,1837
64
+ davidkhala_ai-0.2.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
65
+ davidkhala_ai-0.2.2.dist-info/RECORD,,
@@ -1,191 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import os
4
- from pathlib import Path
5
- from typing import Iterable, TypedDict, Optional
6
- from urllib.parse import urlparse
7
-
8
- import requests
9
-
10
- from davidkhala.ai.agent.dify.api import API, Iterator
11
- from davidkhala.ai.agent.dify.model import Document as DocumentBase
12
-
13
-
14
- class DatasetDict(TypedDict):
15
- id: str
16
- name: str
17
- description: str
18
- provider: str
19
- permission: str
20
- data_source_type: str
21
- indexing_technique: str
22
- doc_form: str
23
- runtime_mode: str
24
- is_published: bool
25
- enable_api: bool
26
- # stats
27
- app_count: int
28
- document_count: int
29
- word_count: int
30
- total_documents: int
31
- total_available_documents: int
32
- # embedding
33
- embedding_available: bool
34
- embedding_model: str
35
- embedding_model_provider: str
36
- retrieval_model_dict: dict
37
- external_retrieval_model: dict
38
- external_knowledge_info: dict
39
-
40
-
41
- class Document(DocumentBase):
42
- data_source_info: dict[str, str]
43
- data_source_detail_dict: dict[str, dict]
44
- dataset_process_rule_id: str
45
- created_from: str
46
- created_by: str
47
- created_at: int
48
- tokens: int
49
- archived: bool
50
- display_status: str
51
- word_count: int
52
- hit_count: int
53
- doc_form: str
54
- doc_metadata: dict
55
- disabled_at: int
56
- disabled_by: str
57
-
58
-
59
- class Dataset(API):
60
- def __init__(self, api_key: str, base_url="https://api.dify.ai/v1"):
61
- super().__init__(api_key, f"{base_url}/datasets")
62
-
63
- def paginate_datasets(self, page=1, size=20):
64
- r = self.request(self.base_url, "GET", params={
65
- 'page': page,
66
- 'limit': size,
67
- })
68
- return r
69
-
70
- def list_datasets(self) -> Iterable[list[DatasetDict]]:
71
- return Iterator(self.paginate_datasets, None)
72
-
73
- @property
74
- def ids(self):
75
- for sub_list in self.list_datasets():
76
- for dataset in sub_list:
77
- yield dataset['id']
78
-
79
- class Instance(API):
80
- def __init__(self, d: Dataset, dataset_id: str):
81
- super().__init__(d.api_key, f"{d.base_url}/{dataset_id}")
82
-
83
- def get(self):
84
- return self.request(self.base_url, "GET")
85
-
86
- def upload(self, filename, *, path=None, url=None, document_id=None):
87
- """
88
- don't work for .html
89
- work for .md
90
- """
91
- files = {}
92
- if path:
93
- with open(path, 'rb') as f:
94
- content = f.read()
95
- if not filename:
96
- filename = os.path.basename(path)
97
- elif url:
98
- r = requests.get(url)
99
- r.raise_for_status()
100
- if not filename:
101
- parsed_url = urlparse(url)
102
- filename = Path(parsed_url.path).name
103
- content = r.content
104
- files['file'] = (filename, content)
105
- if document_id:
106
- # don't work for html
107
- r = requests.post(f"{self.base_url}/documents/{document_id}/update-by-file", files=files,
108
- **self.options)
109
- else:
110
- r = requests.post(f"{self.base_url}/document/create-by-file", files=files, **self.options)
111
- r = self.on_response(r)
112
- return r['document']
113
-
114
- def paginate_documents(self, page=1, size=20):
115
- return self.request(f"{self.base_url}/documents", "GET", params={
116
- 'page': page,
117
- 'limit': size
118
- })
119
-
120
- def list_documents(self) -> Iterable[Document]:
121
- for document_batch in Iterator(self.paginate_documents, None):
122
- for document in document_batch:
123
- yield Document(**document)
124
-
125
- def has_document(self, name) -> bool:
126
- return any(name == item['name'] for row in self.list_documents() for item in row)
127
-
128
-
129
- class ChunkDict(TypedDict):
130
- id: str
131
- position: int
132
- document_id: str
133
- content: str
134
- sign_content: str # trimmed version of content
135
- answer: Optional[str] # only used in QA chunk
136
- word_count: int
137
- tokens: int
138
- keywords: Optional[list[str]]
139
- index_node_id: str # chunk 在向量索引中的节点 ID
140
- index_node_hash: str # hash of sign_content
141
- hit_count: int
142
- enabled: bool
143
- status: str # 'completed'
144
- created_at: int # timestamp
145
- updated_at: int # timestamp
146
- completed_at: int # timestamp
147
- created_by: str # user id
148
- child_chunks: list
149
- error: Optional
150
- stopped_at: Optional[int] # timestamp
151
- disabled_at: Optional[int] # timestamp
152
-
153
-
154
- class Document(API):
155
- def __init__(self, d: Dataset.Instance, document_id: str):
156
- super().__init__(d.api_key, f"{d.base_url}/documents/{document_id}")
157
-
158
- def exist(self):
159
- try:
160
- self.get()
161
- return True
162
- except requests.exceptions.HTTPError as e:
163
- if e.response.status_code == 404:
164
- return False
165
- else:
166
- raise e
167
-
168
- def get(self):
169
- return self.request(self.base_url, "GET")
170
-
171
- def paginate_chunks(self, page=1, size=20):
172
- return self.request(f"{self.base_url}/segments", "GET", params={
173
- 'page': page,
174
- 'limit': size
175
- })
176
-
177
- def list_chunks(self) -> Iterable[ChunkDict]:
178
- for chunk_batch in Iterator(self.paginate_chunks, None):
179
- for chunk in chunk_batch:
180
- yield chunk
181
-
182
- def delete(self):
183
- if self.exist():
184
- self.request(self.base_url, "DELETE")
185
- class Chunk(API):
186
- def __init__(self, d: Document, segment_id: str):
187
- super().__init__(d.api_key, f"{d.base_url}/segments/{segment_id}")
188
- def get(self):
189
- r= self.request(self.base_url, "GET")
190
- assert r['doc_form'] # optional value text_model
191
- return r['data']
@@ -1 +0,0 @@
1
-