save_headhaul_document.py 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. class SaveHeadhaulDocumentTool:
  2. name = 'save_headhaul_document'
  3. route_path = '/mcp/tools/saveHeadhaulDocument'
  4. def __init__(self, api_client=None):
  5. self.api_client = api_client
  6. def metadata(self):
  7. return {
  8. 'name': self.name,
  9. 'description': (
  10. '使用场景:上传成功返回ticket_ref之后,或用户只要外链时,'
  11. '用本工具保存一份新的头程文档。必须带添加文档类型和至少一条已确认种类的录入单号。'
  12. 'ticket_ref和外链不能同时提交。没有行内编辑;要改只能删除后重加。'
  13. '禁止使用:手填OSS路径、混放号码种类、把文件放进工具参数。'
  14. '参数只用于工具内部调用;最终回答只能展示中文业务名称。'
  15. ),
  16. 'input_schema': {
  17. 'type': 'object',
  18. 'properties': {
  19. 'document_type_id': {
  20. 'type': 'integer',
  21. 'minimum': 1,
  22. 'description': (
  23. '要添加的文档类型。必须先调用'
  24. 'list_headhaul_document_filter_options并选择“添加文档类型”。'
  25. ),
  26. },
  27. 'ticket_ref': {
  28. 'type': 'string',
  29. 'minLength': 1,
  30. 'maxLength': 500,
  31. 'description': '上传成功后的一次性凭证,不能手填。',
  32. },
  33. 'link_url': {
  34. 'type': 'string',
  35. 'minLength': 8,
  36. 'maxLength': 1000,
  37. 'description': 'http或https外链;与ticket_ref二选一。',
  38. },
  39. 'entries': {
  40. 'type': 'array',
  41. 'minItems': 1,
  42. 'items': {
  43. 'type': 'object',
  44. 'properties': {
  45. 'entry_no_type': {
  46. 'type': 'integer',
  47. 'minimum': 1,
  48. 'maximum': 24,
  49. },
  50. 'numbers': {
  51. 'type': 'array',
  52. 'minItems': 1,
  53. 'maxItems': 50,
  54. 'items': {
  55. 'type': 'string',
  56. 'minLength': 1,
  57. 'maxLength': 100,
  58. },
  59. },
  60. },
  61. 'required': ['entry_no_type', 'numbers'],
  62. 'additionalProperties': False,
  63. },
  64. 'description': '录入单号。entry_no_type来自录入单号种类筛选项。',
  65. },
  66. },
  67. 'required': ['document_type_id', 'entries'],
  68. 'additionalProperties': False,
  69. },
  70. }
  71. def call(
  72. self,
  73. document_type_id,
  74. entries,
  75. ticket_ref=None,
  76. link_url=None,
  77. request_id='rq_save_headhaul_document',
  78. ):
  79. if self.api_client is None:
  80. raise RuntimeError(
  81. 'api client is required for save_headhaul_document'
  82. )
  83. if (
  84. isinstance(document_type_id, bool)
  85. or not isinstance(document_type_id, int)
  86. or document_type_id < 1
  87. ):
  88. raise ValueError('document_type_id is invalid')
  89. ticket = '' if ticket_ref is None else str(ticket_ref).strip()
  90. link = '' if link_url is None else str(link_url).strip()
  91. if (ticket != '' and link != '') or (ticket == '' and link == ''):
  92. raise ValueError('ticket_ref and link_url are exclusive')
  93. if link and not (
  94. link.lower().startswith('http://')
  95. or link.lower().startswith('https://')
  96. ):
  97. raise ValueError('link_url is invalid')
  98. payload = {
  99. 'document_type_id': document_type_id,
  100. 'entries': self._entries(entries),
  101. }
  102. if ticket:
  103. payload['ticket_ref'] = ticket
  104. if link:
  105. payload['link_url'] = link
  106. return self.api_client.call_tool(
  107. self.name, self.route_path, payload, request_id,
  108. )
  109. @staticmethod
  110. def _entries(values):
  111. if not isinstance(values, list) or not values:
  112. raise ValueError('entries is invalid')
  113. result = []
  114. for item in values:
  115. if not isinstance(item, dict):
  116. raise ValueError('entries is invalid')
  117. type_id = item.get('entry_no_type')
  118. numbers = item.get('numbers')
  119. if (
  120. isinstance(type_id, bool)
  121. or not isinstance(type_id, int)
  122. or type_id < 1
  123. or type_id > 24
  124. ):
  125. raise ValueError('entry_no_type is invalid')
  126. if not isinstance(numbers, list) or not numbers or len(numbers) > 50:
  127. raise ValueError('numbers is invalid')
  128. clean = []
  129. for number in numbers:
  130. if not isinstance(number, str):
  131. raise ValueError('numbers is invalid')
  132. number = number.strip()
  133. if not number or len(number) > 100:
  134. raise ValueError('numbers is invalid')
  135. if number not in clean:
  136. clean.append(number)
  137. result.append({'entry_no_type': type_id, 'numbers': clean})
  138. return result