metaso-sdk 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.
metaso_sdk/__init__.py ADDED
@@ -0,0 +1,24 @@
1
+ """metaso-sdk package.
2
+
3
+ The official Python SDK for https://metaso.cn
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from .model import File, Query, Status, Topic
9
+ from .search import search
10
+ from .subject import create_topic, delete_file, delete_topic, update_progress, upload_directory, upload_file
11
+
12
+ __all__: list[str] = [
13
+ Status,
14
+ Query,
15
+ Topic,
16
+ File,
17
+ search,
18
+ create_topic,
19
+ delete_topic,
20
+ upload_file,
21
+ update_progress,
22
+ delete_file,
23
+ upload_directory,
24
+ ]
metaso_sdk/__main__.py ADDED
@@ -0,0 +1,14 @@
1
+ """Entry-point module, in case you use `python -m metaso_sdk`.
2
+
3
+ Why does this file exist, and why `__main__`? For more info, read:
4
+
5
+ - https://www.python.org/dev/peps/pep-0338/
6
+ - https://docs.python.org/3/using/cmdline.html#cmdoption-m
7
+ """
8
+
9
+ import sys
10
+
11
+ from metaso_sdk.cli import main
12
+
13
+ if __name__ == "__main__":
14
+ sys.exit(main(sys.argv[1:]))
metaso_sdk/cli.py ADDED
@@ -0,0 +1,58 @@
1
+ """Module that contains the command line application."""
2
+
3
+ # Why does this file exist, and why not put this in `__main__`?
4
+ #
5
+ # You might be tempted to import things from `__main__` later,
6
+ # but that will cause problems: the code will get executed twice:
7
+ #
8
+ # - When you run `python -m metaso_sdk` python will execute
9
+ # `__main__.py` as a script. That means there won't be any
10
+ # `metaso_sdk.__main__` in `sys.modules`.
11
+ # - When you import `__main__` it will get executed again (as a module) because
12
+ # there's no `metaso_sdk.__main__` in `sys.modules`.
13
+
14
+ from __future__ import annotations
15
+
16
+ import argparse
17
+ import sys
18
+ from typing import Any
19
+
20
+ from metaso_sdk import debug
21
+
22
+
23
+ class _DebugInfo(argparse.Action):
24
+ def __init__(self, nargs: int | str | None = 0, **kwargs: Any) -> None:
25
+ super().__init__(nargs=nargs, **kwargs)
26
+
27
+ def __call__(self, *args: Any, **kwargs: Any) -> None: # noqa: ARG002
28
+ debug.print_debug_info()
29
+ sys.exit(0)
30
+
31
+
32
+ def get_parser() -> argparse.ArgumentParser:
33
+ """Return the CLI argument parser.
34
+
35
+ Returns:
36
+ An argparse parser.
37
+ """
38
+ parser = argparse.ArgumentParser(prog="metaso")
39
+ parser.add_argument("-V", "--version", action="version", version=f"%(prog)s {debug.get_version()}")
40
+ parser.add_argument("--debug-info", action=_DebugInfo, help="Print debug information.")
41
+ return parser
42
+
43
+
44
+ def main(args: list[str] | None = None) -> int:
45
+ """Run the main program.
46
+
47
+ This function is executed when you type `metaso` or `python -m metaso_sdk`.
48
+
49
+ Parameters:
50
+ args: Arguments passed from the command line.
51
+
52
+ Returns:
53
+ An exit code.
54
+ """
55
+ parser = get_parser()
56
+ opts = parser.parse_args(args=args)
57
+ print(opts)
58
+ return 0
metaso_sdk/client.py ADDED
@@ -0,0 +1,11 @@
1
+ import os
2
+
3
+ import httpx
4
+
5
+ api_key = os.getenv("METASO_API_KEY", "")
6
+
7
+ client = httpx.Client(
8
+ base_url="https://metaso.cn/api/open",
9
+ headers={"Authorization": f"Bearer {api_key}"},
10
+ timeout=60,
11
+ )
metaso_sdk/debug.py ADDED
@@ -0,0 +1,109 @@
1
+ """Debugging utilities."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import platform
7
+ import sys
8
+ from dataclasses import dataclass
9
+ from importlib import metadata
10
+
11
+
12
+ @dataclass
13
+ class Variable:
14
+ """Dataclass describing an environment variable."""
15
+
16
+ name: str
17
+ """Variable name."""
18
+ value: str
19
+ """Variable value."""
20
+
21
+
22
+ @dataclass
23
+ class Package:
24
+ """Dataclass describing a Python package."""
25
+
26
+ name: str
27
+ """Package name."""
28
+ version: str
29
+ """Package version."""
30
+
31
+
32
+ @dataclass
33
+ class Environment:
34
+ """Dataclass to store environment information."""
35
+
36
+ interpreter_name: str
37
+ """Python interpreter name."""
38
+ interpreter_version: str
39
+ """Python interpreter version."""
40
+ interpreter_path: str
41
+ """Path to Python executable."""
42
+ platform: str
43
+ """Operating System."""
44
+ packages: list[Package]
45
+ """Installed packages."""
46
+ variables: list[Variable]
47
+ """Environment variables."""
48
+
49
+
50
+ def _interpreter_name_version() -> tuple[str, str]:
51
+ if hasattr(sys, "implementation"):
52
+ impl = sys.implementation.version
53
+ version = f"{impl.major}.{impl.minor}.{impl.micro}"
54
+ kind = impl.releaselevel
55
+ if kind != "final":
56
+ version += kind[0] + str(impl.serial)
57
+ return sys.implementation.name, version
58
+ return "", "0.0.0"
59
+
60
+
61
+ def get_version(dist: str = "metaso-sdk") -> str:
62
+ """Get version of the given distribution.
63
+
64
+ Parameters:
65
+ dist: A distribution name.
66
+
67
+ Returns:
68
+ A version number.
69
+ """
70
+ try:
71
+ return metadata.version(dist)
72
+ except metadata.PackageNotFoundError:
73
+ return "0.0.0"
74
+
75
+
76
+ def get_debug_info() -> Environment:
77
+ """Get debug/environment information.
78
+
79
+ Returns:
80
+ Environment information.
81
+ """
82
+ py_name, py_version = _interpreter_name_version()
83
+ packages = ["metaso-sdk"]
84
+ variables = ["PYTHONPATH", *[var for var in os.environ if var.startswith("METASO_SDK")]]
85
+ return Environment(
86
+ interpreter_name=py_name,
87
+ interpreter_version=py_version,
88
+ interpreter_path=sys.executable,
89
+ platform=platform.platform(),
90
+ variables=[Variable(var, val) for var in variables if (val := os.getenv(var))],
91
+ packages=[Package(pkg, get_version(pkg)) for pkg in packages],
92
+ )
93
+
94
+
95
+ def print_debug_info() -> None:
96
+ """Print debug/environment information."""
97
+ info = get_debug_info()
98
+ print(f"- __System__: {info.platform}")
99
+ print(f"- __Python__: {info.interpreter_name} {info.interpreter_version} ({info.interpreter_path})")
100
+ print("- __Environment variables__:")
101
+ for var in info.variables:
102
+ print(f" - `{var.name}`: `{var.value}`")
103
+ print("- __Installed packages__:")
104
+ for pkg in info.packages:
105
+ print(f" - `{pkg.name}` v{pkg.version}")
106
+
107
+
108
+ if __name__ == "__main__":
109
+ print_debug_info()
metaso_sdk/model.py ADDED
@@ -0,0 +1,35 @@
1
+ from typing import Optional
2
+
3
+ from pydantic import BaseModel
4
+
5
+
6
+ class Status(BaseModel):
7
+ errCode: int
8
+ errMsg: str
9
+
10
+
11
+ class Query(BaseModel):
12
+ question: str
13
+ lang: str = "zh"
14
+ sessionId: Optional[str] = None
15
+ stream: bool = False
16
+ topicId: Optional[str] = None
17
+ searchTopicId: Optional[str] = None
18
+
19
+
20
+ class Topic(BaseModel):
21
+ id: Optional[str] = None
22
+ name: str
23
+ dirRootId: Optional[str] = None
24
+ description: Optional[str] = None
25
+
26
+
27
+ class File(BaseModel):
28
+ id: Optional[str]
29
+ fileName: str
30
+ parentId: str
31
+ contentType: str
32
+ size: int
33
+ previewUrl: Optional[str] = None
34
+ originalUrl: Optional[str] = None
35
+ progress: int
metaso_sdk/py.typed ADDED
File without changes
metaso_sdk/search.py ADDED
@@ -0,0 +1,35 @@
1
+ import json
2
+
3
+ from httpx_sse import EventSource, connect_sse
4
+
5
+ from .client import client
6
+ from .model import Query, Topic
7
+
8
+ EventSource._check_content_type = lambda self: True
9
+
10
+
11
+ def search(query: Query, *, stream: bool = False, topic: Topic = None):
12
+ """搜索功能的函数注释。
13
+
14
+ :param query: 查询对象,用于指定搜索内容。
15
+ :param stream: 布尔值,表示是否以流的形式返回结果,默认为False。
16
+ :param topic: 专题对象,用于指定搜索的专题,默认为None。
17
+ :return: 根据查询条件返回搜索结果。
18
+ """
19
+ if topic is not None:
20
+ query.searchTopicId = topic.id
21
+
22
+ if stream:
23
+ query.stream = True
24
+
25
+ def _gen():
26
+ with connect_sse(client, "POST", "/search/v2", json=query.model_dump()) as event_source:
27
+ for sse in event_source.iter_sse():
28
+ if (data := sse.data) != "[DONE]":
29
+ yield json.loads(data)
30
+
31
+ if query.stream:
32
+ return _gen()
33
+
34
+ resp = client.post("/search/v2", json=query.model_dump())
35
+ return resp.json()["data"]
metaso_sdk/subject.py ADDED
@@ -0,0 +1,98 @@
1
+ from pathlib import Path
2
+ from typing import List, Optional
3
+
4
+ from streamable import Stream
5
+
6
+ from .client import client
7
+ from .model import File, Status, Topic
8
+
9
+
10
+ def create_topic(topic: Topic) -> Optional[Topic]:
11
+ """创建一个新专题。
12
+
13
+ :param topic (Topic): 要创建的专题对象。
14
+ :return: 专题对象。
15
+ """
16
+ resp = client.put("/topic", json=topic.model_dump())
17
+ json = resp.json()
18
+ status = Status.model_validate(json)
19
+ if status.errCode == 0:
20
+ topic = Topic.model_validate(json["data"])
21
+ return topic
22
+
23
+
24
+ def delete_topic(topic: Topic) -> bool:
25
+ """删除指定的专题。
26
+
27
+ :param topic (Topic): 要删除的Topic对象。
28
+ :return: 如果成功删除Topic返回True,否则返回False。
29
+ """
30
+ resp = client.post("/topic/trash", json={"ids": [topic.id]})
31
+ status = Status.model_validate(resp.json())
32
+ return status.errCode == 0
33
+
34
+
35
+ def upload_file(topic: Topic, file) -> Optional[File]:
36
+ """上传文件到指定专题。
37
+
38
+ :param topic (Topic): 文件所属的专题。
39
+ :param file: 待上传的文件对象。
40
+ :return: 如果文件上传成功,返回文件对象;否则返回None。
41
+ """
42
+ resp = client.put(f"/file/{topic.dirRootId}", files={"file": file})
43
+ json = resp.json()
44
+ status = Status.model_validate(json)
45
+ if status.errCode == 0:
46
+ file = File.model_validate(json["data"][0])
47
+ return file
48
+
49
+
50
+ def update_progress(file: File) -> File:
51
+ """更新文件处理进度。
52
+
53
+ :param file: 需要更新进度的文件对象。
54
+ :return: 更新后的文件对象。
55
+ """
56
+ resp = client.get(f"/file/{file.id}/progress")
57
+ file.progress = resp.json()["data"]
58
+ return file
59
+
60
+
61
+ def delete_file(file: File) -> bool:
62
+ """删除指定的文件。
63
+
64
+ :param file: 要删除的File对象。
65
+ :return: 如果成功删除File返回True,否则返回False。
66
+ """
67
+ resp = client.post("/file/trash", json={"ids": [file.id]})
68
+ status = Status.model_validate(resp.json())
69
+ return status.errCode == 0
70
+
71
+
72
+ def upload_directory(topic: Topic, path: Path, pattern="**/*", *, concurrency=10) -> List[File]:
73
+ """递归上传指定目录下的文件到指定专题。
74
+
75
+ 参数:
76
+ - topic: 目标专题。
77
+ - path: 需要上传的本地目录路径。
78
+ - pattern: 文件匹配模式,默认为"**/*",表示匹配所有文件。
79
+ - concurrency: 并发上传的数量,默认为10。
80
+
81
+ 返回:
82
+ - List[File]: 成功上传的文件列表。
83
+ """
84
+
85
+ def _upload_file(file) -> File:
86
+ with file.open("rb") as f:
87
+ return upload_file(topic, f)
88
+
89
+ files = list(
90
+ Stream(Path(path).glob(pattern))
91
+ .filter(Path.is_file)
92
+ .map(_upload_file, concurrency=concurrency)
93
+ .filter(lambda file: file is not None)
94
+ .observe("files")
95
+ .catch(),
96
+ )
97
+
98
+ return files
@@ -0,0 +1,111 @@
1
+ Metadata-Version: 2.1
2
+ Name: metaso-sdk
3
+ Version: 0.1.0
4
+ Summary: The official Python SDK for https://metaso.cn
5
+ Author-Email: Zhao Xiaohong <zhaoxiaohong@metasota.ai>
6
+ License: MIT
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: Programming Language :: Python
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3 :: Only
12
+ Classifier: Programming Language :: Python :: 3.8
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Programming Language :: Python :: 3.14
19
+ Classifier: Topic :: Documentation
20
+ Classifier: Topic :: Software Development
21
+ Classifier: Topic :: Utilities
22
+ Classifier: Typing :: Typed
23
+ Project-URL: Homepage, https://meta-sota.github.io/metaso-sdk
24
+ Project-URL: Documentation, https://meta-sota.github.io/metaso-sdk
25
+ Project-URL: Changelog, https://meta-sota.github.io/metaso-sdk/changelog
26
+ Project-URL: Repository, https://github.com/meta-sota/metaso-sdk
27
+ Project-URL: Issues, https://github.com/meta-sota/metaso-sdk/issues
28
+ Project-URL: Discussions, https://github.com/meta-sota/metaso-sdk/discussions
29
+ Project-URL: Gitter, https://gitter.im/metaso-sdk/community
30
+ Project-URL: Funding, https://github.com/sponsors/meta-sota
31
+ Requires-Python: >=3.8
32
+ Requires-Dist: httpx-sse>=0.4.0
33
+ Requires-Dist: httpx>=0.27.2
34
+ Requires-Dist: pydantic>=2.10.0
35
+ Requires-Dist: streamable>=1.3.5
36
+ Description-Content-Type: text/markdown
37
+
38
+ # metaso-sdk
39
+
40
+ [![ci](https://github.com/meta-sota/metaso-sdk/workflows/ci/badge.svg)](https://github.com/meta-sota/metaso-sdk/actions?query=workflow%3Aci)
41
+ [![documentation](https://img.shields.io/badge/docs-mkdocs-708FCC.svg?style=flat)](https://meta-sota.github.io/metaso-sdk/)
42
+ [![pypi version](https://img.shields.io/pypi/v/metaso-sdk.svg)](https://pypi.org/project/metaso-sdk/)
43
+ [![gitter](https://badges.gitter.im/join%20chat.svg)](https://app.gitter.im/#/room/#metaso-sdk:gitter.im)
44
+
45
+ The official Python SDK for https://metaso.cn
46
+
47
+ ## 安装
48
+
49
+ ```bash
50
+ pip install metaso-sdk
51
+ ```
52
+
53
+ ## 配置 METASO_API_KEY
54
+
55
+ metaso-sdk 从环境变量 `METASO_API_KEY` 读取用于认证的 API 密钥,可以在 shell 里进行设置:
56
+
57
+ ```bash
58
+ export METASO_API_KEY="mk-EE2..."
59
+ ```
60
+
61
+ 或者在 Python 代码里进行设置:
62
+
63
+ ```python
64
+ import os
65
+ os.environ["METASO_API_KEY"] = "mk-EE2..."
66
+ ```
67
+
68
+ ## 搜索
69
+
70
+ ### 搜索问题
71
+ ```python
72
+ from metaso_sdk import search, Query
73
+ search(Query(question="abc"))
74
+ ```
75
+
76
+ ### 追问
77
+
78
+ ```python
79
+ search(Query(question="广播公司", sessionId='8550018047390023680'))
80
+ ```
81
+
82
+ ### 流式返回
83
+
84
+ ```python
85
+ for chunk in search(Query(question="abc"), stream=True):
86
+ print(chunk)
87
+
88
+ ...
89
+ {'type': 'heartbeat'}
90
+ {'text': '因此,“abc”可以指代字母表的前三个字母、美籍华裔、美国广播公司、一种音乐记谱法以及一种编程语言。具体含义需要根据上下文来确定。', 'type': 'append-text'}
91
+ ```
92
+
93
+ ## 专题
94
+
95
+ ### 递归上传文件夹
96
+
97
+ ```
98
+ from metaso_sdk import create_topic, upload_directory, Topic
99
+
100
+ topic = create_topic(Topic(name="functional programing"))
101
+ files = upload_directory(topic, "dir")
102
+ ```
103
+
104
+ ### 搜索特定专题
105
+
106
+ ```python
107
+ from metaso_sdk import search, Query
108
+
109
+ query = Query(question="functional")
110
+ search(query, topic=topic)
111
+ ```
@@ -0,0 +1,14 @@
1
+ metaso_sdk-0.1.0.dist-info/METADATA,sha256=p6elhZYx8vXqoMtjv16dinGrmC1NmE96kfVLH_SIBAE,3420
2
+ metaso_sdk-0.1.0.dist-info/WHEEL,sha256=thaaA2w1JzcGC48WYufAs8nrYZjJm8LqNfnXFOFyCC4,90
3
+ metaso_sdk-0.1.0.dist-info/entry_points.txt,sha256=VqvzYeWOYZKCpogIumetYL8h5UZsI93oLI15kJjbwj4,63
4
+ metaso_sdk-0.1.0.dist-info/licenses/LICENSE,sha256=7nWd1jm_pniRaOJtzgZnzECn_eeGLoPMvPTjCyRpj1A,1065
5
+ metaso_sdk/__init__.py,sha256=ojrRidgqOO2LWNh0q7lv897364hE61yck2Kh7OW8mZ8,488
6
+ metaso_sdk/__main__.py,sha256=WCRS7Ehbf26-NWDSnt-Zmy0eP63paf67q9eYV4V_Q3g,345
7
+ metaso_sdk/cli.py,sha256=Z7I54ojBpWxQ_Pce7-XM7gucneDIpC21yAqRGkYrm9I,1734
8
+ metaso_sdk/client.py,sha256=j2hC1NBbxsRFHC4hOSD06YrfV5JLffgrA-H5iGNBPq4,204
9
+ metaso_sdk/debug.py,sha256=K3ypLgs78a8bhiAHE_5fZZAL-U622fuKPyEdL7M1UW8,2831
10
+ metaso_sdk/model.py,sha256=oXLuFZMkZczRxOyWu2enRtM5eDKrNsMgDg0O2KWkX64,674
11
+ metaso_sdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ metaso_sdk/search.py,sha256=3iA9lIncpAcbPHPby0XnTYaYvhM2983bEOBa40Nu_kY,1082
13
+ metaso_sdk/subject.py,sha256=0CsDo1PRZlagR4GKLDbJXvK2Q6qFAd4_3VV8NTNYPeE,2877
14
+ metaso_sdk-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: pdm-backend (2.4.3)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,5 @@
1
+ [console_scripts]
2
+ metaso = metaso_sdk.cli:main
3
+
4
+ [gui_scripts]
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 MetaSota
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.