fiuai-sdk-python 0.8.5__tar.gz → 0.8.7__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.
Files changed (41) hide show
  1. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/PKG-INFO +1 -1
  2. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/pyproject.toml +1 -1
  3. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/client.py +22 -2
  4. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/profile.py +117 -3
  5. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/.gitignore +0 -0
  6. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/CHANGELOG.md +0 -0
  7. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/LICENSE +0 -0
  8. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/README.md +0 -0
  9. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/__init__.py +0 -0
  10. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/auth/__init__.py +0 -0
  11. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/auth/context_mgr.py +0 -0
  12. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/auth/header.py +0 -0
  13. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/auth/helper.py +0 -0
  14. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/auth/type.py +0 -0
  15. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/bank.py +0 -0
  16. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/company.py +0 -0
  17. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/config.py +0 -0
  18. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/const.py +0 -0
  19. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/context.py +0 -0
  20. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/datatype.py +0 -0
  21. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/doctype.py +0 -0
  22. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/error.py +0 -0
  23. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/examples/fastapi_integration.py +0 -0
  24. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/http/__init__.py +0 -0
  25. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/http/client.py +0 -0
  26. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/item.py +0 -0
  27. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/perm.py +0 -0
  28. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/pkg/cache/__init__.py +0 -0
  29. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/pkg/cache/cache_client.py +0 -0
  30. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/pkg/cache/circuit_breaker.py +0 -0
  31. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/pkg/cache/decorator.py +0 -0
  32. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/pkg/cache/redis_manager.py +0 -0
  33. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/pkg/cache/types.py +0 -0
  34. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/resp.py +0 -0
  35. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/setup.py +0 -0
  36. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/type.py +0 -0
  37. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/util.py +0 -0
  38. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/utils/__init__.py +0 -0
  39. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/utils/ids.py +0 -0
  40. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/utils/logger.py +0 -0
  41. {fiuai_sdk_python-0.8.5 → fiuai_sdk_python-0.8.7}/src/fiuai_sdk_python/utils/text.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fiuai_sdk_python
3
- Version: 0.8.5
3
+ Version: 0.8.7
4
4
  Summary: FiuAI Python SDK - 企业级AI服务集成开发工具包
5
5
  Project-URL: Homepage, https://github.com/fiuai/fiuai-sdk-python
6
6
  Project-URL: Documentation, https://github.com/fiuai/fiuai-sdk-python#readme
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fiuai_sdk_python"
3
- version = "0.8.5"
3
+ version = "0.8.7"
4
4
  description = "FiuAI Python SDK - 企业级AI服务集成开发工具包"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -2,7 +2,7 @@ import httpx
2
2
  from tenacity import retry, stop_after_attempt, wait_exponential
3
3
  import json
4
4
  from urllib.parse import quote
5
- from typing import Any, Literal, List
5
+ from typing import Any, Dict, List, Literal, Optional
6
6
 
7
7
  from .util import get_client_config, is_initialized
8
8
  from .error import FiuaiGeneralError, FiuaiAuthError
@@ -12,7 +12,6 @@ from .profile import UserProfileInfo
12
12
  from .auth import AuthHeader
13
13
  from .resp import parse_response, ApiResponse
14
14
  from .context import get_current_headers
15
- from typing import Dict, Any, Optional
16
15
 
17
16
 
18
17
  logger = getLogger(__name__)
@@ -298,6 +297,27 @@ class FiuaiSDK(object):
298
297
  res = self.client.get(self.url + '/api/v2/internal/doctype/get', params=d, headers=headers)
299
298
  return self.post_process(res)
300
299
 
300
+ def get_decrypted_password(
301
+ self,
302
+ doctype: str,
303
+ name: str,
304
+ fieldname: str = "password",
305
+ extra_headers: Optional[Dict[str, Any]] = None,
306
+ ) -> Optional[str]:
307
+ """从 Frappe __Auth 读取 Password 类型字段明文 (internal get_password)."""
308
+ params = {"doctype": doctype, "name": name, "fieldname": fieldname}
309
+ headers = self._get_merged_headers(extra_headers)
310
+ res = self.client.get(
311
+ self.url + "/api/v2/internal/doctype/get_password",
312
+ params=params,
313
+ headers=headers,
314
+ )
315
+ resp = self.post_process(res)
316
+ if resp.is_success() and resp.data and isinstance(resp.data, dict):
317
+ pw = resp.data.get("password")
318
+ return pw if isinstance(pw, str) else None
319
+ return None
320
+
301
321
  def internal_get_list(self, doctype, filters=None, fields=None, limit_start=0, limit_page_length=20, order_by=None,
302
322
  extra_headers: Optional[Dict[str, Any]] = None) -> ApiResponse:
303
323
  d = {
@@ -14,9 +14,70 @@
14
14
  # Copyright (c) 2025 FiuAI
15
15
 
16
16
 
17
- from pydantic import BaseModel, Field
18
- from typing import List, Literal
17
+ from pydantic import BaseModel, Field, field_validator, model_validator
18
+ from typing import List, Literal, Optional, Self
19
19
  from .utils.text import safe_string_name
20
+ from enum import StrEnum
21
+
22
+ class TaxPayerType(StrEnum):
23
+ GeneralTaxPayer = "general_tax_payer"
24
+ SmallTaxPayer = "small_tax_payer"
25
+ OtherTaxPayer = "other_tax_payer"
26
+
27
+
28
+ PROVINCE_CODE = {
29
+ "China": [
30
+ ["1100", "北京市"],
31
+ ["1200", "天津市"],
32
+ ["1300", "河北省"],
33
+ ["1400", "山西省"],
34
+ ["1500", "内蒙古自治区"],
35
+ ["2100", "辽宁省"],
36
+ ["2200", "吉林省"],
37
+ ["2300", "黑龙江省"],
38
+ ["3100", "上海市"],
39
+ ["3200", "江苏省"],
40
+ ["3300", "浙江省"],
41
+ ["3400", "安徽省"],
42
+ ["3500", "福建省"],
43
+ ["3600", "江西省"],
44
+ ["3700", "山东省"],
45
+ ["4100", "河南省"],
46
+ ["4200", "湖北省"],
47
+ ["4300", "湖南省"],
48
+ ["4400", "广东省"],
49
+ ["4500", "广西壮族自治区"],
50
+ ["4600", "海南省"],
51
+ ["5000", "重庆市"],
52
+ ["5100", "四川省"],
53
+ ["5200", "贵州省"],
54
+ ["5300", "云南省"],
55
+ ["5400", "西藏自治区"],
56
+ ["6100", "陕西省"],
57
+ ["6200", "甘肃省"],
58
+ ["6300", "青海省"],
59
+ ["6400", "宁夏回族自治区"],
60
+ ["6500", "新疆维吾尔自治区"],
61
+ ["7100", "台湾省"],
62
+ ["8100", "香港特别行政区"],
63
+ ["8200", "澳门特别行政区"],
64
+ ],
65
+ }
66
+
67
+ _CHINA_PROVINCE_CODES: frozenset[str] = frozenset(
68
+ row[0] for row in PROVINCE_CODE["China"]
69
+ )
70
+
71
+
72
+ def _is_china_country_region(region: str) -> bool:
73
+ r = region.strip()
74
+ if not r:
75
+ return False
76
+ else:
77
+ if r.casefold() == "china":
78
+ return True
79
+ else:
80
+ return False
20
81
 
21
82
 
22
83
  class UserCompanyInfo(BaseModel):
@@ -85,8 +146,59 @@ class UserCurrentCompanyInfo(BaseModel):
85
146
  default_currency: str = Field(description="货币")
86
147
  company_size: str = Field(description="公司规模")
87
148
  entity_type: Literal["Enterprise", "Individual", "Other"] = Field(description="公司类型")
149
+ tax_payer_type: Optional[TaxPayerType] = Field(default=None, description="纳税人类型,一般纳税人,小规模纳税人,其他")
150
+ tax_source_location: Optional[str] = Field(default=None, description="税务登记地,中国特有配置,按省级行政区划分,比如江苏是3200")
88
151
  company_profile: str = Field(description="公司业务特点提示词", default="")
89
-
152
+
153
+ @field_validator("tax_payer_type", mode="before")
154
+ @classmethod
155
+ def validate_tax_payer_type_when_present(cls, value: object) -> TaxPayerType | None:
156
+ if value is None or value == "":
157
+ return None
158
+ if isinstance(value, TaxPayerType):
159
+ return value
160
+ if isinstance(value, str):
161
+ try:
162
+ return TaxPayerType(value)
163
+ except ValueError:
164
+ allowed = ", ".join(repr(m.value) for m in TaxPayerType)
165
+ raise ValueError(
166
+ f"tax_payer_type must be one of [{allowed}], got {value!r}"
167
+ ) from None
168
+ raise TypeError(
169
+ f"tax_payer_type must be str, TaxPayerType or None, got {type(value).__name__}"
170
+ )
171
+
172
+ @field_validator("tax_source_location", mode="before")
173
+ @classmethod
174
+ def normalize_tax_source_location(cls, value: object) -> str | None:
175
+ if value is None:
176
+ return None
177
+ if not isinstance(value, str):
178
+ raise TypeError(
179
+ "tax_source_location must be str or None, "
180
+ f"got {type(value).__name__}"
181
+ )
182
+ stripped = value.strip()
183
+ return None if stripped == "" else stripped
184
+
185
+ @model_validator(mode="after")
186
+ def validate_tax_source_location_against_china_provinces(self) -> Self:
187
+ loc = self.tax_source_location
188
+ china = _is_china_country_region(self.country_region)
189
+
190
+ if loc is not None and not china:
191
+ raise ValueError(
192
+ "tax_source_location is only applicable when country_region is China, CN or 中国"
193
+ )
194
+ if loc is not None and china and loc not in _CHINA_PROVINCE_CODES:
195
+ raise ValueError(
196
+ "tax_source_location must be a valid China province admin code "
197
+ f"(e.g. 3200 for Jiangsu), got {loc!r}"
198
+ )
199
+ return self
200
+
201
+
90
202
  class UserProfileInfo(BaseModel):
91
203
  user_base_info: UserBaseInfo = Field(description="用户基础信息")
92
204
  user_permissions: UserPermissionInfo = Field(description="用户权限")
@@ -123,6 +235,8 @@ class UserProfileInfo(BaseModel):
123
235
  default_currency=self.user_current_company_info.default_currency,
124
236
  company_size=self.user_current_company_info.company_size,
125
237
  entity_type=self.user_current_company_info.entity_type,
238
+ tax_payer_type=self.user_current_company_info.tax_payer_type,
239
+ tax_source_location=self.user_current_company_info.tax_source_location,
126
240
  company_profile=self.user_current_company_info.company_profile,
127
241
  ),
128
242
  )