Metadata-Version: 2.1
Name: asktable
Version: 2.0.7
Summary: AskTable SDK
Home-page: https://asktable.com/
Author: lele
Description-Content-Type: text/markdown
Requires-Dist: requests
Requires-Dist: tabulate
Requires-Dist: pybase62

# Python Client SDK（Python客户端SDK）

## 1 APIs（API 接口）

> API 接口列表： https://api.asktable.com/

## 2 认证方法
需要在 Header 中增加 Authorization 字段，值为 `Bearer <api_key>`，其中 `<api_key>` 为用户的 API Key。

注意：新用户请联系 `contact@datamini.ai` 获得 API Key。

## 3 安装

依赖 Python 3.9+。

```shell
pip install asktable
```


## 4 快速开始

   - **命令行CLI**
       ```shell
       $ pip install asktable
       $ asktable -k <api_key>

       -- AskTable 客户端(0.7.19)初始化完成！
       -- 连接服务器(-a)： https://api.asktable.com/
       -- 使用API Key (-k)： api_key01
       -- 您可以使用 'at' 来访问 AskTable，比如：通过 'at.datasources' 来查询数据源列表
          完整的使用方法，请参考帮助文档：https://pypi.org/project/asktable/


       In [1]: at.datasources.latest.ask('你好')
       Out[1]: [OK-2s]|| 你好！有什么可以帮助你的吗？
       ```

- **Python SDK**

    ```python
    from asktable import AskTable
    at = AskTable(api_key='your_api_key')
    ds = at.datasources.create_from_local_file("your_excel_file_path")
    ds.ask('总共有多少行？')
    ```

## 5 完整示例

以下示例使用命令行CLI方式演示。

### 5.1 注册数据源

1. 注册 MySQL 数据源

    ```python
    at.datasources.register(
        engine="mysql",
        access_config={
            "host": "localhost",
            "port": 3306,
            "user": "root",
            "password": "",
            "db": "test"  # 可选
            "securetunnel_id": "atst_p94FEWbXDWWDW3VC" #可选
        }
    )
    Data Source ds_2MSqC5EUFawpK0nVOg8MXN: Wait for 1 seconds and check again. Current status: processing
    Data Source ds_2MSqC5EUFawpK0nVOg8MXN: Wait for 2 seconds and check again. Current status: processing
    Data Source ds_2MSqC5EUFawpK0nVOg8MXN: Wait for 3 seconds and check again. Current status: processing
    Data Source ds_2MSqC5EUFawpK0nVOg8MXN: Wait for 5 seconds and check again. Current status: processing
    Data Source ds_2MSqC5EUFawpK0nVOg8MXN: Wait for 8 seconds and check again. Current status: processing
    Data Source ds_2MSqC5EUFawpK0nVOg8MXN  AI Process Success.
    ```

`access_config` 中的 `securetunnel_id` 为可选项，如果您的数据源需要通过 ATST 访问，请填写此字段。AskTable 会通过您的这个 ATST 来访问这个数据源，确保数据传输的安全性。

2. 注册一个可下载的文件(Excel或CSV)
    ```python
    at.datasources.register(
        engine="csv",
        access_config={
            "location_url": "https://example.com/path/to/myfile.csv",
            "location_type": "http",
        }
    )
    ```

3. 上传并注册一个本地的文件（Excel或CSV）
    ```python
    In [69]: at.datasources.create_from_local_file(local_file_path="/DataMini/asktable/sample_data/tests/test_chart.xlsx", direct_to_oss=True)
    Out[69]: <ds_mrQq88Pu2DaBLCUZwgBAO: 月销售数据 >
    ```

### 5.2 查看和使用数据源

1. 所有数据源
    ```python
    In [54]: at.datasources
    Out[54]:
    id                         created_at           name                                     type
    ds_1MEd5IA392sgt7mw5PcwgV  2024-01-21T17:09:18  高校讲师工作量汇总                       excel
    ds_5DuPVZbOo5iRx9ZRWNtDCj  2024-01-22T15:44:52  求公式数据汇总                           excel
    ds_2Vl6Y5bypIJE0J19ulmiBL  2024-01-28T19:03:02  2024天津公务员招考                       excel
    ds_3IrvrL6oFd1SIQ6FEKUPCn  2024-02-03T11:34:45  学生成绩总览                             excel
    ds_2TDFez0l0qiSwFLsGvRKvV  2024-02-25T14:40:14  杭州房产信息                             csv
    ```

2. 最新的数据源

    ```python
    In [55]: at.datasources.latest
    Out[55]: <ds_2TDFez0l0qiSwFLsGvRKvV: 杭州房产信息 >

    ```

3. 根据ID精确查找
    ```python
    In [56]: at.datasources.get('ds_2TDFez0l0qiSwFLsGvRKvV')
    Out[56]: <ds_2TDFez0l0qiSwFLsGvRKvV: 杭州房产信息 >

    ```

4. 根据Name查找（可能重名，返回List）
    ```python
    In [57]: at.datasources.get(name='杭州房产信息')
    Out[57]:
    [<ds_25vqFPq3wAgzcCVGoygBOO: 杭州房产信息 >,
     <ds_2eGWHUvUbWN0AAbke0aKIb: 杭州房产信息 >,
     <ds_2TDFez0l0qiSwFLsGvRKvV: 杭州房产信息 >]
    ```

5. 删除数据源

    ```python
    In [65]: ds = at.datasources.latest.delete()  # 删除最新的数据源
    Out[65]: True
    ```
   同时会删除这个数据源对应的 AskTable SecureTunnel Link（ATST Link）。


### 5.3 查看和管理数据源的Meta信息

1. 查看Meta（在AskTable中管理的Meta）
    ```python
    In [15]: at.datasources.latest.meta
    Out[15]:
    test (测试环境数据库)
    ------------------------------
    Table Name       Table Desc            Fields(analysed/all)
    alembic_version  数据库迁移版本控制表  1/1
    chats            聊天记录表            4/4
    users            用户信息表            2/2
    ```

2. 查看 RuntimeMeta（运行时，即原文件或者原数据库中最新的Meta）
    ```python
    In [16]: at.datasources.latest.meta_runtime
    Out[16]:
    test
    ------------------------------
    Table Name       Table Desc    Fields(analysed/all)
    alembic_version                0/1
    chats                          1/4
    users                          1/2
    ```
3. 更新Meta（即将 RuntimeMeta 更新到 AskTable 中，并自动分析和识别含义）
    ```python
    In [17]: at.datasources.latest.meta.update()
    Data Source ds_39dJslzX5G2TDofefHJ0t9 Meta 状态为 processing，正在等待完成...
    等待 1 秒后重新检查。当前状态: processing
    Data Source ds_39dJslzX5G2TDofefHJ0t9 Meta 更新成功。
    Out[17]:
    test (测试环境数据库)
    ------------------------------
    Table Name       Table Desc            Fields(analysed/all)
    alembic_version  数据库迁移版本控制表  1/1
    chats            聊天记录表            4/4
    users            用户信息表            2/2
    ```

4. 查看 Meta 状态
    ```python
    In [21]: at.datasources.latest.meta.status
    Out[21]: 'success'
    ```

5. 删除 Meta（将AskTable中保存的Meta删除）
    ```python
    In [18]: at.datasources.latest.meta.delete()
    Out[18]: True
    ```

6. 查看示例问题
    ```python
    In [62]: ds = at.datasources.latest

    In [63]: ds.sample_questions
    Out[63]: '- 查询杭州房产的平均总价\n- 统计各区域房产数量 \n（为了方便沟通，您可以直接指定列名：城市, 区域, 子区域, 小区, 总价, 单价, 户型, 楼层, 建筑面积, 套内面积, 装修, 梯户比例, 电梯, 别墅类型, 挂牌日期, 产权, 房屋用途, 房龄, 链接）'
    ```


### 5.4 发起和管理对话

1. 基于一个数据源，发起会话并提问
    ```python
    In [8]: answer = at.datasources.latest.ask('你好')
    Out[8]: [msg_5PvPIvwPofYG9HkUzjzzRc] [ai] [OK-10s] 你好！有什么可以帮助你的吗？   [Just now]>
    ```

2. 选择多个数据源，一起提问
    ```python
    In [9]: chat = at.chats.create(['ds_2MSqC5EUFawpK0nVOg8MXN', 'ds_34LANTyIJi0srCJrHFxRZ'])

    In [10]: answer = chat.ask('你好')

    In [11]: answer.to_dict()
    Out[11]:
    {'id': 'msg_4wQf4lD2AqDiPikRW4Tijk',
     'chat_id': 'chat_G5g0FKbGQlljAKHrKz160',
     'created': 1712287283,
     'role': 'ai',
     'content': {'status': 'OK',
      'elapsed_time': 2,
      'text': '你好！有什么可以帮到您的吗？',
      'answer_file_url': None,
      'answer_image_url': None,
      'structure_queries': None,
      'statistics': None},
     'reply_to_msg_id': 'msg_7aEuVCH8fX9BzlS3EvwKnl'}
    ```

3. 查看所有对话
    ```python
    In [40]: at.chats
    Out[40]:
    id                           created              datasource_ids                   human_msgs    ai_msgs  latest_msg
    chat_E90pdsNUlDE7wo8gsjahf   2024-01-06 12:10:00  ['ds_1ZAoAyIn1ph0lldief4cP5']             2          2  2024-01-07 01:26:48
    chat_ql7vWZEIqafqg9dMAaNpi   2024-01-07 05:35:03  ['ds_S0tdSsWcuB2HxEA1BQICE']              0          0
    chat_1SfzrRkiIlKyvHtOTbX7qW  2024-01-07 05:46:21  ['ds_6tWxUjnKbK2JwJRmLuPTj9']             4          4  2024-01-07 05:50:59
    chat_2WEHFozMZPFQhYkqLwvufZ  2024-01-07 10:34:15  ['ds_5Jydks0MbCJ3atBIHBC98']              1          1  2024-01-07 10:35:02
    ```
4. 获取最后一个对话
    ```python
    In [41]: at.chats.latest
    Out[41]: <Chat chat_6LYoQImReUun8gR8q32LZm [2024-02-06 06:33:47]>
    ```

5. 根据ID查找对话
    ```python
    In [44]: at.chats.get('chat_1SfzrRkiIlKyvHtOTbX7qW')
    Out[44]: <Chat chat_1SfzrRkiIlKyvHtOTbX7qW [2024-01-07 05:46:21]>
    ```

6. 查看某个对话的历史消息

    ```python
    In [43]: at.chats.latest.messages
    Out[43]:
    id                          created                 role    content
    msg_1x0t6ch4KzZiSPFDbrL1iC  2 hours 59 minutes ago  human   将B列的数据拆分为姓名、手机号和地址三列
    msg_5D5bdJ2vBYjhpjlVuqyN5N  2 hours 59 minutes ago  ai      [OK-22s] 很抱歉，由于元数据中并未提供包含电话号码和地址的字段信息，我们无法直接从数据库中分割列B的数据为姓名、电话号码和地址三个独立的列。如果您能提供更详细的数据格式或者具体的分割规则，我们可能会有其他方式来帮助您处理这个问题。
    msg_1rnZp94PzQeMetZxV2W3VC  2 hours 57 minutes ago  human   看起来好像可以按照换行来拆分
    msg_1igXXP1VBMSVx2iW8DENVt  2 hours 57 minutes ago  ai      [OK-34s] 已经根据您的要求将B列的数据拆分为姓名、手机号和地址三列。您可以查看处理好的数据。 [File:https://0vc.cc/KcZzK]
    ```

7. 删除某个对话

    ```python
    In [45]: chat = at.chats.latest

    In [46]: chat.delete()
    Out[46]: True
    ```

### 5.5 AskTable Secure Tunnel (ATST) 安全隧道
AskTable Secure Tunnel (ATST) 是一种安全工具，允许 AskTable 服务通过安全隧道与内部本地数据库进行安全通信。此服务适用于需要确保数据传输安全性的企业环境。

1. 启动一个新的 AskTable Secure Tunnel 之前，需要获得一个唯一的 `securetunnel_id`。

   ```python
   from asktable import AskTable
   at = AskTable(api_key='your_api_key')

   # 创建新的安全隧道Key
   atst = at.securetunnels.create()
   print(f"SecureTunnel Created: {atst.key}")
   ```

2. **查看所有 ATST** 列出所有已创建的 ATST 信息。

   ```python
       In [2]: at.securetunnels
       Out[2]:
       key                         created_at
       atst_p94PzQeMetZxV2W3VC    2024-01-06 12:10:00
       atst_p94PzQeMetZxV2W3VC    2024-01-07 05:35:03
       atst_p94PzQeMetZxV2W3VC  2024-01-07 05:46:21
       atst_p94PzQeMetZxV2W3VC  2024-01-07 10:34:15
   ```

3. 获取 ATST 的配置信息

    ```python
        In [2]: at.securetunnels.get('atst_p94PzQeMetZxV2W3VC')
        Out[2]: {
        "id": "atst_p94PzQeMetZxV2W3VC",
        "atst_server_host": "atst-server.asktable.com",
        "atst_server_port": 7077,
        "links": [
            {
                "id": "link_1",
                "target_host": "10.1.2.3",
                "target_port": 3306,
                "proxy_port": 6001,
            },
            {
                "id": "link_1",
                "target_host": "10.1.2.4",
                "target_port": 3306,
                "proxy_port": 6002,
            },
        ]}
    ```

4. 获取某个ATST上绑定的所有Link

```python
    In [3]: at.securetunnels.get('atst_p94PzQeMetZxV2W3VC').links
    Out[3]:
```

### 5.6 设置角色和权限

AskTable 的访问控制是通过访问策略定义和角色定义来实现的。


1. 创建访问策略
访问策略（Access Policy）定义的是某个人能访问（或不能访问）的数据集。

   ```python
   from asktable import AskTable
   at = AskTable(api_key='your_api_key')

   # 允许访问用户表，但只能访问某个城市的数据
   policy = at.policies.create(
       permission="allow",
       name="user_policy_shanghai",
       datasource_ids="ds_2MSqC5EUFawpK0nVOg8MXN,ds_34LANTyIJi0srCJrHFxRZ",
       tables_regex_pattern="^(user)_.*$",
       fields_regex_pattern=".*",
       rows_filters=[
           "ds_1.*.user.city_id = {city_id}"
       ]
   )
   print(f"Policy Created: {policy.name}")

   ```

2. 创建角色。这使得可以根据角色控制其数据访问权限。

    ```python
    # 创建角色示例
    role = at.roles.create(
       name="city_manager",
       description="Manager role for a city",
    )
    print(f"Role Created: {role.name}")

    # 为角色绑定策略
    role.attach_policies(policy_names=[policy.name])

    print(f"Policies Attached: {policy.name}")

    ```

3. 通过扮演角色来发起对话。这个角色决定了用户在该会话中可以访问哪些数据。

   ```python
   # 使用角色访问数据示例
   chat = at.chats.create(
       ['ds_2MSqC5EUFawpK0nVOg8MXN', 'ds_34LANTyIJi0srCJrHFxRZ'],
       {
           "role_name": "city_manager",
           "role_variables": {
               "user_id": "123",
               "city_id": "310"
           }
       }
   )
   response = chat.ask('Show me the user data for Shanghai.')
   print(response)
   ```

4. 查看所有的策略

   ```python
   In [1]: at.policies
   Out[1]:
   id                          name      datasource_ids            schemas_regex_pattern    tables_regex_pattern    fields_regex_pattern    rows_filters    modified_at
   policy_7ANpC68WihJm0zkyRll5Do  policy1  ds_UJDPngVNVLn7aKwvvWGTx                                                                                              2024-05-05T10:49:29
   policy_7ANpC68WihJm0zkyRll5Do  policy2  ds_UJDPngVNVLn7aKwvvWGTx                                                                                              2024-05-05T10:49:29
   ```

5. 查看和管理某个策略
    ```python
    # 根据ID
    policy1 = at.policies.get('policy_7ANpC68WihJm0zkyRll5Do')
    # 根据Name
    policy2 = at.policies.get(name='policy1')
    # 获取最新的策略
    policy3 = at.policies.latest

    # 修改
    policy1.update(name='policy2', fields_regex_pattern='.*')

    # 删除
    policy1.delete()
    ```

6. 查看所有的角色
   ```python
   In [8]: at.roles
   Out[8]:
   id                           name    modified_at
   role_41OHLVTiDZNDBukDhrMiJk  role1   2024-05-05T10:53:34
   ```

7. 获取和管理某个角色
    ```python
    # 根据ID
    role = at.roles.get('role_41OHLVTiDZNDBukDhrMiJk')
    # 根据Name
    role = at.roles.get(name='role1')
    # 获取最新的角色
    role = at.roles.latest

    # 修改角色
    role.update(name='role2')

    # 删除角色
    role.delete()
    ```

8. 获取和管理某个角色的策略
   ```python
   # 全部
   policies = role.policies
   # 根据策略ID
   policy = role.policies.get('policy_6m2DGTvM6tC64cyF2cYjAJ')

   # 删除策略
   policy.delete()
   ```

## 6. 在创建 Chat 的时候指定用户信息

   ```python
   # 给出用户的个人信息
   chat = at.chats.create(
       ['ds_2MSqC5EUFawpK0nVOg8MXN', 'ds_34LANTyIJi0srCJrHFxRZ'],
       user_profile={"name": "张三", "dm_uid": "dm_uid_7JJna0yGz753J9i3S00A6h"}
   )
   response = chat.ask('我总共发了多少消息')
   print(response)
   ```

## 7. 在创建 Chat 的时候指定可以执行的APIs
   ```python
   # 定义可以访问的 API
   chat = at.chats.create(
       ['ds_2MSqC5EUFawpK0nVOg8MXN', 'ds_34LANTyIJi0srCJrHFxRZ'],
       extapi_ids=["extapi_1", "extapi_2"]
   )
   response = chat.ask('帮我订个会议室，明天下午 3 点到 4点')
   print(response)
   ```

## 8. 错误处理

### 8.0 OK 正确返回

```python
In [25]: at.chats.get('chat_5lSeeLn7KMScVCYjVNewFS').messages
Out[25]:
id                          created        role    content                                                                                           reply_to_msg_id
msg_43ZYqK5kMjr3xMIPqxgVxq  5 minutes ago  human   最晚入职是谁
msg_4Bf8mRqPYk5U9bS99qFsT4  4 minutes ago  ai      [OK-7s] 最晚入职的员工是陈敏，员工编号为5，身份证号为100200200001012222，联系电话为13800000001。  msg_43ZYqK5kMjr3xMIPqxgVxq

In [26]: at.chats.get('chat_5lSeeLn7KMScVCYjVNewFS').messages.get('msg_4Bf8mRqPYk5U9bS99qFsT4')
Out[26]: [msg_4Bf8mRqPYk5U9bS99qFsT4] [ai] [OK-7s] 最晚入职的员工是陈敏，员工编号为5，身份证号为100200200001012222，联系电话为13800000001。   [4 minutes ago]>

In [27]: msg = at.chats.get('chat_5lSeeLn7KMScVCYjVNewFS').messages.get('msg_4Bf8mRqPYk5U9bS99qFsT4')

In [28]: msg.answer.to_dict()
Out[28]:
{'status': 'OK',
 'elapsed_time': 7,
 'text': '最晚入职的员工是陈敏，员工编号为5，身份证号为100200200001012222，联系电话为13800000001。',
 'files': None,
 'charts': None,
 'query_insights': [{'question': '查询数据库中最晚入职的员工信息。\r\n\r',
   'datasource': 'ds_1pv7a93n2LQr5DHHX64ECI',
   'results': [{'query': 'SELECT * FROM test.employees ORDER BY employee_id DESC LIMIT 1',
     'result_info': {'row_count': 1,
      'column_count': 4,
      'columns': ['employee_id', 'name', 'id_number', 'phone_number']},
     'query_time_ms': 38}],
   'query_time': 3}],
 'payload': {'recognized_addresses': None, 'error_type': '', 'error_msg': ''}}
```

### 8.1 QAErrorCannotHandle 无法处理

```python

In [18]: at.chats.latest.messages
Out[18]:
id                          created    role    content                      reply_to_msg_id
msg_40rEzS9Pi6cKA910qZe9PN  Just now   human   年龄最大的员工是谁
msg_3zbA7gvdJFiUG0x5xM7fAj  Just now   ai      [ERROR-6s] 没有找到相关信息  msg_40rEzS9Pi6cKA910qZe9PN

In [19]: msg = at.chats.latest.messages.get('msg_3zbA7gvdJFiUG0x5xM7fAj')

In [20]: msg
Out[20]: [msg_3zbA7gvdJFiUG0x5xM7fAj] [ai] [ERROR-6s] 没有找到相关信息   [Just now]>

In [21]: msg.answer.to_dict()
Out[21]:
{'status': 'ERROR',
 'elapsed_time': 6,
 'text': '没有找到相关信息',
 'files': None,
 'charts': None,
 'query_insights': None,
 'payload': {'recognized_addresses': None,
  'error_type': 'QAErrorCannotHandle',
  'error_msg': "The 'age' field is not present in the employees table metadata provided."}}

```


以上步骤展示了如何在 AskTable 中通过角色和权限管理功能来精细控制数据访问权限，从而确保数据隐私和安全。
