sciverse 0.7.2__tar.gz → 0.8.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sciverse
3
- Version: 0.7.2
3
+ Version: 0.8.0
4
4
  Summary: Sciverse Agent Tools — OpenAI/Anthropic/LangChain compatible tool schema and async client for Sciverse retrieval APIs
5
5
  Project-URL: Homepage, https://github.com/opendatalab/Sciverse-Agent-Tools
6
6
  Project-URL: Repository, https://github.com/opendatalab/Sciverse-Agent-Tools.git
@@ -131,6 +131,14 @@ await c.search_papers(
131
131
  page_size=10,
132
132
  )
133
133
 
134
+ # 1b. Entity collections — search authors / journals (set collection)
135
+ await c.search_papers(
136
+ collection="authors", # papers (default) / authors / sources
137
+ filters_advanced=[{"field": "summary_stats.h_index", "operator": "FILTER_OP_GTE", "value": 50}],
138
+ sort_advanced=[{"field": "cited_by_count", "order": "SORT_ORDER_DESC"}],
139
+ )
140
+ # Call list_catalog(collection="authors") to discover each collection's fields.
141
+
134
142
  # 2. Natural-language semantic search (vector + BM25 hybrid, returns chunks)
135
143
  await c.semantic_search(query="How does attention work?", top_k=10, mode="balanced")
136
144
 
@@ -114,6 +114,14 @@ await c.search_papers(
114
114
  page_size=10,
115
115
  )
116
116
 
117
+ # 1b. Entity collections — search authors / journals (set collection)
118
+ await c.search_papers(
119
+ collection="authors", # papers (default) / authors / sources
120
+ filters_advanced=[{"field": "summary_stats.h_index", "operator": "FILTER_OP_GTE", "value": 50}],
121
+ sort_advanced=[{"field": "cited_by_count", "order": "SORT_ORDER_DESC"}],
122
+ )
123
+ # Call list_catalog(collection="authors") to discover each collection's fields.
124
+
117
125
  # 2. Natural-language semantic search (vector + BM25 hybrid, returns chunks)
118
126
  await c.semantic_search(query="How does attention work?", top_k=10, mode="balanced")
119
127
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sciverse"
3
- version = "0.7.2"
3
+ version = "0.8.0"
4
4
  description = "Sciverse Agent Tools — OpenAI/Anthropic/LangChain compatible tool schema and async client for Sciverse retrieval APIs"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -142,6 +142,7 @@ def _cmd_search(args: argparse.Namespace) -> int:
142
142
  if args.title_contains: kwargs["title_contains"] = args.title_contains
143
143
  if args.abstract_contains: kwargs["abstract_contains"] = args.abstract_contains
144
144
  if args.sort_by_year != "none": kwargs["sort_by_year"] = args.sort_by_year
145
+ if getattr(args, "collection", "papers") != "papers": kwargs["collection"] = args.collection
145
146
  if args.freshness_boost != "NONE": kwargs["freshness_boost"] = args.freshness_boost
146
147
  kwargs["page"] = args.page
147
148
  kwargs["page_size"] = args.page_size
@@ -173,7 +174,10 @@ def _cmd_content(args: argparse.Namespace) -> int:
173
174
 
174
175
  def _cmd_catalog(args: argparse.Namespace) -> int:
175
176
  async def _do(client):
176
- r = await client.list_catalog(include_sample_values=args.samples)
177
+ r = await client.list_catalog(
178
+ include_sample_values=args.samples,
179
+ collection=getattr(args, "collection", None),
180
+ )
177
181
  _print_json(r)
178
182
 
179
183
  return _run_with_client(_do)
@@ -241,6 +245,9 @@ def _build_parser() -> argparse.ArgumentParser:
241
245
  ),
242
246
  )
243
247
  search.add_argument("query", nargs="?", default="", help="全文 BM25 关键词(可选)")
248
+ search.add_argument("--collection", choices=["papers", "authors", "sources"], default="papers",
249
+ help="实体集合(默认 papers)。authors/sources 的高级字段过滤请用 SDK 的 "
250
+ "filters_advanced / sort_advanced;CLI 主要支持 query 与 catalog 自省")
244
251
  search.add_argument("--author", "--authors", action="append", default=[], dest="authors",
245
252
  help="作者名(可多次传,任一命中即可)")
246
253
  search.add_argument("--year-from", type=int, dest="year_from", help="起始年(含)")
@@ -283,6 +290,8 @@ def _build_parser() -> argparse.ArgumentParser:
283
290
  "catalog",
284
291
  help="字段 introspection(GET /meta-catalog),第一次接入时建议先调",
285
292
  )
293
+ cat.add_argument("--collection", choices=["papers", "authors", "sources"], default="papers",
294
+ help="字段目录所属实体集合(默认 papers)")
286
295
  cat.add_argument("--samples", action="store_true",
287
296
  help="拉枚举字段的取值样本(首次 OpenSearch terms agg,~几百 ms;之后 24h 缓存)")
288
297
  cat.set_defaults(func=_cmd_catalog)
@@ -13,7 +13,7 @@ _PLATFORM = platform.system().lower() # "linux" | "darwin" | "windows"
13
13
  _SOURCE = f"{_PLATFORM}-{_CHANNEL}"
14
14
 
15
15
 
16
- _PASSTHROUGH = ("query", "page", "page_size", "fields", "freshness_boost")
16
+ _PASSTHROUGH = ("query", "page", "page_size", "fields", "freshness_boost", "collection")
17
17
  _FRESHNESS_BOOST_VALUES = frozenset({"NONE", "MILD", "STRONG"})
18
18
 
19
19
 
@@ -65,6 +65,11 @@ def _to_backend_payload(kwargs: dict[str, Any]) -> dict[str, Any]:
65
65
  "order": "SORT_ORDER_DESC" if sort_by_year == "desc" else "SORT_ORDER_ASC",
66
66
  })
67
67
 
68
+ if (sa := kwargs.get("sort_advanced")) is not None:
69
+ for item in sa:
70
+ if item and item.get("field"):
71
+ sort.append({"field": item["field"], "order": item.get("order", "SORT_ORDER_DESC")})
72
+
68
73
  if filters:
69
74
  out["filters"] = filters
70
75
  if sort:
@@ -151,14 +156,25 @@ class AgentToolsClient:
151
156
  resp.raise_for_status()
152
157
  return resp.json()
153
158
 
154
- async def list_catalog(self, *, include_sample_values: bool = False) -> dict[str, Any]:
159
+ async def list_catalog(
160
+ self,
161
+ *,
162
+ include_sample_values: bool = False,
163
+ include_field_stats: bool = False,
164
+ collection: str | None = None,
165
+ ) -> dict[str, Any]:
155
166
  """对应 GET /meta-catalog。
156
167
 
157
168
  返回字段 catalog:每个字段的名称 / 类型 / filterable / sortable / default_returned /
158
169
  描述 / 适用 FilterOperator,外加 enum-like 字段的样本值(include_sample_values=True 时)。
170
+ collection 指定实体集合(papers 默认 / authors / sources),各 collection 字段集不同。
159
171
  Agent 第一次接触 Sciverse 或碰到字段不确定时建议先调一次再构造 search_papers。
160
172
  """
161
- params = {"include_sample_values": str(include_sample_values).lower()}
173
+ params: dict[str, str] = {"include_sample_values": str(include_sample_values).lower()}
174
+ if include_field_stats:
175
+ params["include_field_stats"] = "true"
176
+ if collection:
177
+ params["collection"] = collection
162
178
  resp = await self._client.get("/meta-catalog", params=params)
163
179
  resp.raise_for_status()
164
180
  return resp.json()
@@ -1,7 +1,7 @@
1
1
  """Auto-generated. Do not edit. Run scripts/build.sh."""
2
2
  import json
3
3
 
4
- TOOLS_VERSION = "0.7.2"
4
+ TOOLS_VERSION = "0.8.0"
5
5
 
6
6
  OPENAI_TOOLS = json.loads(r"""
7
7
  [
@@ -13,6 +13,17 @@ OPENAI_TOOLS = json.loads(r"""
13
13
  "parameters": {
14
14
  "type": "object",
15
15
  "properties": {
16
+ "collection": {
17
+ "type": "string",
18
+ "enum": [
19
+ "papers",
20
+ "authors",
21
+ "sources"
22
+ ],
23
+ "default": "papers",
24
+ "description": "检索的实体集合。papers(默认,论文)/ authors(作者)/ sources(来源期刊)。 各 collection 字段集不同,用 list_catalog(collection=<name>)学习对应 schema。 注意:本工具的便捷字段(authors/journals/year_from/subjects 等)只对 papers 有意义; 查 authors/sources 时改用 filters_advanced + 该 collection 的字段名(如 authors 的 summary_stats.h_index / orcid,sources 的 issn / is_oa)。authors 用 orcid、 sources 用 issn 与论文检索结果关联。",
25
+ "x-en-description": "Entity collection to search. papers (default) / authors / sources. Each collection has its own field schema — call list_catalog(collection=<name>). The convenience fields (authors/journals/year_from/subjects) apply to papers only; for authors/sources use filters_advanced with that collection's field names."
26
+ },
16
27
  "query": {
17
28
  "type": "string",
18
29
  "description": "BM25 全文关键词,匹配标题/摘要/期刊名/关键词字段。留空则纯靠结构化过滤。"
@@ -69,6 +80,7 @@ OPENAI_TOOLS = json.loads(r"""
69
80
  },
70
81
  "operator": {
71
82
  "type": "string",
83
+ "description": "过滤操作符。MATCH(分词模糊)适用于 author、keywords(输入 \"Hinton\" 命中 \"Geoffrey Hinton\"); MATCH_PHRASE(短语模糊)适用于 publication_venue_name_unified,整词连续匹配(\"Nature\" 命中 \"Nature Communications\";非前缀匹配,\"Nature Comm\" 不会命中); doi 用 EQ,服务端归一化(去 doi.org 前缀+转小写)后精确匹配。MATCH/MATCH_PHRASE 仅对配了 text 子字段的字段有效。",
72
84
  "enum": [
73
85
  "FILTER_OP_EQ",
74
86
  "FILTER_OP_NE",
@@ -78,7 +90,9 @@ OPENAI_TOOLS = json.loads(r"""
78
90
  "FILTER_OP_LTE",
79
91
  "FILTER_OP_IN",
80
92
  "FILTER_OP_NIN",
81
- "FILTER_OP_CONTAINS"
93
+ "FILTER_OP_CONTAINS",
94
+ "FILTER_OP_MATCH",
95
+ "FILTER_OP_MATCH_PHRASE"
82
96
  ],
83
97
  "default": "FILTER_OP_EQ"
84
98
  },
@@ -86,6 +100,30 @@ OPENAI_TOOLS = json.loads(r"""
86
100
  }
87
101
  }
88
102
  },
103
+ "sort_advanced": {
104
+ "type": "array",
105
+ "description": "高级排序逃生舱(按任意可排序字段)。papers 用 sort_by_year 即可; authors/sources 想按 h-index / 被引 / works_count 排序时用本字段。 与 query 互斥(query 走相关性排序)。",
106
+ "items": {
107
+ "type": "object",
108
+ "required": [
109
+ "field",
110
+ "order"
111
+ ],
112
+ "properties": {
113
+ "field": {
114
+ "type": "string"
115
+ },
116
+ "order": {
117
+ "type": "string",
118
+ "enum": [
119
+ "SORT_ORDER_DESC",
120
+ "SORT_ORDER_ASC"
121
+ ],
122
+ "default": "SORT_ORDER_DESC"
123
+ }
124
+ }
125
+ }
126
+ },
89
127
  "sort_by_year": {
90
128
  "type": "string",
91
129
  "enum": [
@@ -175,16 +213,71 @@ OPENAI_TOOLS = json.loads(r"""
175
213
  "parameters": {
176
214
  "type": "object",
177
215
  "properties": {
216
+ "collection": {
217
+ "type": "string",
218
+ "enum": [
219
+ "papers",
220
+ "authors",
221
+ "sources"
222
+ ],
223
+ "default": "papers",
224
+ "description": "字段 catalog 所属实体集合。papers(默认)/ authors / sources,各 collection 字段不同。"
225
+ },
178
226
  "include_sample_values": {
179
227
  "type": "boolean",
180
228
  "default": false,
181
229
  "description": "是否拉取 enum-like 字段的取值样本。false 仅返回静态 schema(毫秒级);true 触发 OpenSearch terms agg(首次几百毫秒,之后 24h 走缓存)。"
230
+ },
231
+ "include_field_stats": {
232
+ "type": "boolean",
233
+ "default": false,
234
+ "description": "是否返回字段统计(keyword 字段基数 + 数值字段 min/max/avg/p50/p95)。触发 OpenSearch 聚合,缓存 24h。"
182
235
  }
183
236
  },
184
237
  "required": []
185
238
  }
186
239
  }
187
240
  },
241
+ {
242
+ "type": "function",
243
+ "function": {
244
+ "name": "list_paper_relations",
245
+ "description": "分页返回某篇论文的引用关系完整列表。citations/references/related_works 是无界数组\n(高被引文献可达数千条),search_papers 只内联截断少量,要翻完整列表用本接口。\n适用:「论文 X 引用了哪些文献」(relation=REFERENCES)、「哪些文献引用了论文 X」\n(relation=CITATIONS)、「与论文 X 相关的工作」(relation=RELATED_WORKS)。\n注意:CITATIONS(被引:谁引用了我)与 REFERENCES(参考文献:我引用了谁)方向相反。\n典型链路:先 search_papers / semantic_search 拿到 unique_id,再用本接口按 relation 分页。",
246
+ "parameters": {
247
+ "type": "object",
248
+ "required": [
249
+ "unique_id",
250
+ "relation"
251
+ ],
252
+ "properties": {
253
+ "unique_id": {
254
+ "type": "string",
255
+ "description": "目标论文 unique_id(如 paper:10.1038/xxx),来自 search_papers / semantic_search;勿传 doc_id。"
256
+ },
257
+ "relation": {
258
+ "type": "string",
259
+ "enum": [
260
+ "CITATIONS",
261
+ "REFERENCES",
262
+ "RELATED_WORKS"
263
+ ],
264
+ "description": "关系类型。CITATIONS=被引(谁引用了我);REFERENCES=参考文献(我引用了谁);RELATED_WORKS=相关工作。"
265
+ },
266
+ "page": {
267
+ "type": "integer",
268
+ "default": 1,
269
+ "minimum": 1
270
+ },
271
+ "page_size": {
272
+ "type": "integer",
273
+ "default": 25,
274
+ "minimum": 1,
275
+ "maximum": 200
276
+ }
277
+ }
278
+ }
279
+ }
280
+ },
188
281
  {
189
282
  "type": "function",
190
283
  "function": {
@@ -245,6 +338,17 @@ ANTHROPIC_TOOLS = json.loads(r"""
245
338
  "input_schema": {
246
339
  "type": "object",
247
340
  "properties": {
341
+ "collection": {
342
+ "type": "string",
343
+ "enum": [
344
+ "papers",
345
+ "authors",
346
+ "sources"
347
+ ],
348
+ "default": "papers",
349
+ "description": "检索的实体集合。papers(默认,论文)/ authors(作者)/ sources(来源期刊)。 各 collection 字段集不同,用 list_catalog(collection=<name>)学习对应 schema。 注意:本工具的便捷字段(authors/journals/year_from/subjects 等)只对 papers 有意义; 查 authors/sources 时改用 filters_advanced + 该 collection 的字段名(如 authors 的 summary_stats.h_index / orcid,sources 的 issn / is_oa)。authors 用 orcid、 sources 用 issn 与论文检索结果关联。",
350
+ "x-en-description": "Entity collection to search. papers (default) / authors / sources. Each collection has its own field schema — call list_catalog(collection=<name>). The convenience fields (authors/journals/year_from/subjects) apply to papers only; for authors/sources use filters_advanced with that collection's field names."
351
+ },
248
352
  "query": {
249
353
  "type": "string",
250
354
  "description": "BM25 全文关键词,匹配标题/摘要/期刊名/关键词字段。留空则纯靠结构化过滤。"
@@ -301,6 +405,7 @@ ANTHROPIC_TOOLS = json.loads(r"""
301
405
  },
302
406
  "operator": {
303
407
  "type": "string",
408
+ "description": "过滤操作符。MATCH(分词模糊)适用于 author、keywords(输入 \"Hinton\" 命中 \"Geoffrey Hinton\"); MATCH_PHRASE(短语模糊)适用于 publication_venue_name_unified,整词连续匹配(\"Nature\" 命中 \"Nature Communications\";非前缀匹配,\"Nature Comm\" 不会命中); doi 用 EQ,服务端归一化(去 doi.org 前缀+转小写)后精确匹配。MATCH/MATCH_PHRASE 仅对配了 text 子字段的字段有效。",
304
409
  "enum": [
305
410
  "FILTER_OP_EQ",
306
411
  "FILTER_OP_NE",
@@ -310,7 +415,9 @@ ANTHROPIC_TOOLS = json.loads(r"""
310
415
  "FILTER_OP_LTE",
311
416
  "FILTER_OP_IN",
312
417
  "FILTER_OP_NIN",
313
- "FILTER_OP_CONTAINS"
418
+ "FILTER_OP_CONTAINS",
419
+ "FILTER_OP_MATCH",
420
+ "FILTER_OP_MATCH_PHRASE"
314
421
  ],
315
422
  "default": "FILTER_OP_EQ"
316
423
  },
@@ -318,6 +425,30 @@ ANTHROPIC_TOOLS = json.loads(r"""
318
425
  }
319
426
  }
320
427
  },
428
+ "sort_advanced": {
429
+ "type": "array",
430
+ "description": "高级排序逃生舱(按任意可排序字段)。papers 用 sort_by_year 即可; authors/sources 想按 h-index / 被引 / works_count 排序时用本字段。 与 query 互斥(query 走相关性排序)。",
431
+ "items": {
432
+ "type": "object",
433
+ "required": [
434
+ "field",
435
+ "order"
436
+ ],
437
+ "properties": {
438
+ "field": {
439
+ "type": "string"
440
+ },
441
+ "order": {
442
+ "type": "string",
443
+ "enum": [
444
+ "SORT_ORDER_DESC",
445
+ "SORT_ORDER_ASC"
446
+ ],
447
+ "default": "SORT_ORDER_DESC"
448
+ }
449
+ }
450
+ }
451
+ },
321
452
  "sort_by_year": {
322
453
  "type": "string",
323
454
  "enum": [
@@ -401,15 +532,67 @@ ANTHROPIC_TOOLS = json.loads(r"""
401
532
  "input_schema": {
402
533
  "type": "object",
403
534
  "properties": {
535
+ "collection": {
536
+ "type": "string",
537
+ "enum": [
538
+ "papers",
539
+ "authors",
540
+ "sources"
541
+ ],
542
+ "default": "papers",
543
+ "description": "字段 catalog 所属实体集合。papers(默认)/ authors / sources,各 collection 字段不同。"
544
+ },
404
545
  "include_sample_values": {
405
546
  "type": "boolean",
406
547
  "default": false,
407
548
  "description": "是否拉取 enum-like 字段的取值样本。false 仅返回静态 schema(毫秒级);true 触发 OpenSearch terms agg(首次几百毫秒,之后 24h 走缓存)。"
549
+ },
550
+ "include_field_stats": {
551
+ "type": "boolean",
552
+ "default": false,
553
+ "description": "是否返回字段统计(keyword 字段基数 + 数值字段 min/max/avg/p50/p95)。触发 OpenSearch 聚合,缓存 24h。"
408
554
  }
409
555
  },
410
556
  "required": []
411
557
  }
412
558
  },
559
+ {
560
+ "name": "list_paper_relations",
561
+ "description": "分页返回某篇论文的引用关系完整列表。citations/references/related_works 是无界数组\n(高被引文献可达数千条),search_papers 只内联截断少量,要翻完整列表用本接口。\n适用:「论文 X 引用了哪些文献」(relation=REFERENCES)、「哪些文献引用了论文 X」\n(relation=CITATIONS)、「与论文 X 相关的工作」(relation=RELATED_WORKS)。\n注意:CITATIONS(被引:谁引用了我)与 REFERENCES(参考文献:我引用了谁)方向相反。\n典型链路:先 search_papers / semantic_search 拿到 unique_id,再用本接口按 relation 分页。",
562
+ "input_schema": {
563
+ "type": "object",
564
+ "required": [
565
+ "unique_id",
566
+ "relation"
567
+ ],
568
+ "properties": {
569
+ "unique_id": {
570
+ "type": "string",
571
+ "description": "目标论文 unique_id(如 paper:10.1038/xxx),来自 search_papers / semantic_search;勿传 doc_id。"
572
+ },
573
+ "relation": {
574
+ "type": "string",
575
+ "enum": [
576
+ "CITATIONS",
577
+ "REFERENCES",
578
+ "RELATED_WORKS"
579
+ ],
580
+ "description": "关系类型。CITATIONS=被引(谁引用了我);REFERENCES=参考文献(我引用了谁);RELATED_WORKS=相关工作。"
581
+ },
582
+ "page": {
583
+ "type": "integer",
584
+ "default": 1,
585
+ "minimum": 1
586
+ },
587
+ "page_size": {
588
+ "type": "integer",
589
+ "default": 25,
590
+ "minimum": 1,
591
+ "maximum": 200
592
+ }
593
+ }
594
+ }
595
+ },
413
596
  {
414
597
  "name": "read_content",
415
598
  "description": "按字节区间读取文献原文片段。通常配合 semantic_search 返回的 doc_id/offset 使用,\n用于扩展上下文(往前/往后读更多字节)。\n返回:UTF-8 文本片段、bytes_returned、next_offset、是否还有后续。",
@@ -9,7 +9,21 @@ from typing import Any
9
9
  from pydantic import BaseModel, Field
10
10
 
11
11
 
12
+ class Collection(Enum):
13
+ """
14
+ 检索的实体集合。papers(默认,论文)/ authors(作者)/ sources(来源期刊)。 各 collection 字段集不同,用 list_catalog(collection=<name>)学习对应 schema。 注意:本工具的便捷字段(authors/journals/year_from/subjects 等)只对 papers 有意义; 查 authors/sources 时改用 filters_advanced + 该 collection 的字段名(如 authors 的 summary_stats.h_index / orcid,sources 的 issn / is_oa)。authors 用 orcid、 sources 用 issn 与论文检索结果关联。
15
+ """
16
+
17
+ papers = "papers"
18
+ authors = "authors"
19
+ sources = "sources"
20
+
21
+
12
22
  class Operator(Enum):
23
+ """
24
+ 过滤操作符。MATCH(分词模糊)适用于 author、keywords(输入 "Hinton" 命中 "Geoffrey Hinton"); MATCH_PHRASE(短语模糊)适用于 publication_venue_name_unified,整词连续匹配("Nature" 命中 "Nature Communications";非前缀匹配,"Nature Comm" 不会命中); doi 用 EQ,服务端归一化(去 doi.org 前缀+转小写)后精确匹配。MATCH/MATCH_PHRASE 仅对配了 text 子字段的字段有效。
25
+ """
26
+
13
27
  FILTER_OP_EQ = "FILTER_OP_EQ"
14
28
  FILTER_OP_NE = "FILTER_OP_NE"
15
29
  FILTER_OP_GT = "FILTER_OP_GT"
@@ -19,14 +33,29 @@ class Operator(Enum):
19
33
  FILTER_OP_IN = "FILTER_OP_IN"
20
34
  FILTER_OP_NIN = "FILTER_OP_NIN"
21
35
  FILTER_OP_CONTAINS = "FILTER_OP_CONTAINS"
36
+ FILTER_OP_MATCH = "FILTER_OP_MATCH"
37
+ FILTER_OP_MATCH_PHRASE = "FILTER_OP_MATCH_PHRASE"
22
38
 
23
39
 
24
40
  class FiltersAdvancedItem(BaseModel):
25
41
  field: str
26
- operator: Operator | None = "FILTER_OP_EQ"
42
+ operator: Operator | None = Field(
43
+ "FILTER_OP_EQ",
44
+ description='过滤操作符。MATCH(分词模糊)适用于 author、keywords(输入 "Hinton" 命中 "Geoffrey Hinton"); MATCH_PHRASE(短语模糊)适用于 publication_venue_name_unified,整词连续匹配("Nature" 命中 "Nature Communications";非前缀匹配,"Nature Comm" 不会命中); doi 用 EQ,服务端归一化(去 doi.org 前缀+转小写)后精确匹配。MATCH/MATCH_PHRASE 仅对配了 text 子字段的字段有效。',
45
+ )
27
46
  value: Any
28
47
 
29
48
 
49
+ class Order(Enum):
50
+ SORT_ORDER_DESC = "SORT_ORDER_DESC"
51
+ SORT_ORDER_ASC = "SORT_ORDER_ASC"
52
+
53
+
54
+ class SortAdvancedItem(BaseModel):
55
+ field: str
56
+ order: Order
57
+
58
+
30
59
  class SortByYear(Enum):
31
60
  desc = "desc"
32
61
  asc = "asc"
@@ -48,6 +77,10 @@ class FreshnessBoost(Enum):
48
77
 
49
78
 
50
79
  class SearchPapersRequest(BaseModel):
80
+ collection: Collection | None = Field(
81
+ "papers",
82
+ description="检索的实体集合。papers(默认,论文)/ authors(作者)/ sources(来源期刊)。 各 collection 字段集不同,用 list_catalog(collection=<name>)学习对应 schema。 注意:本工具的便捷字段(authors/journals/year_from/subjects 等)只对 papers 有意义; 查 authors/sources 时改用 filters_advanced + 该 collection 的字段名(如 authors 的 summary_stats.h_index / orcid,sources 的 issn / is_oa)。authors 用 orcid、 sources 用 issn 与论文检索结果关联。",
83
+ )
51
84
  query: str | None = Field(
52
85
  None,
53
86
  description="BM25 全文关键词,匹配标题/摘要/期刊名/关键词字段。留空则纯靠结构化过滤。",
@@ -74,6 +107,10 @@ class SearchPapersRequest(BaseModel):
74
107
  filters_advanced: list[FiltersAdvancedItem] | None = Field(
75
108
  None, description="高级过滤逃生舱(仅当上述字段不够用时使用)。"
76
109
  )
110
+ sort_advanced: list[SortAdvancedItem] | None = Field(
111
+ None,
112
+ description="高级排序逃生舱(按任意可排序字段)。papers 用 sort_by_year 即可; authors/sources 想按 h-index / 被引 / works_count 排序时用本字段。 与 query 互斥(query 走相关性排序)。",
113
+ )
77
114
  sort_by_year: SortByYear | None = "desc"
78
115
  freshness_boost: FreshnessBoost | None = Field(
79
116
  "NONE",
@@ -83,6 +120,11 @@ class SearchPapersRequest(BaseModel):
83
120
  page_size: int | None = Field(10, ge=1, le=50)
84
121
 
85
122
 
123
+ class AuthorItem(BaseModel):
124
+ name: str | None = None
125
+ orcid: str | None = None
126
+
127
+
86
128
  class PaperMetadata(BaseModel):
87
129
  """
88
130
  文献元数据。字段名与 metadata-service 后端 `fields.py` 的真实字段一致,
@@ -106,9 +148,9 @@ class PaperMetadata(BaseModel):
106
148
  description="全文 artifact 的内容哈希(sha256)。仅当文档存在全文时返回;元数据-only 记录无此字段,且无法用 doc_id 过滤命中。",
107
149
  )
108
150
  title: str
109
- author: list[str] | None = Field(
151
+ author: list[AuthorItem] | None = Field(
110
152
  None,
111
- description="作者列表(fields.py 中字段名是 author 单数,但类型是 List[string])。",
153
+ description='作者列表(OS object 数组,子字段 name/orcid)。按作者名检索:精确走 author.name.keyword、模糊 MATCH 走 author.name;对外过滤仍传 field "author"。',
112
154
  )
113
155
  abstract: str | None = None
114
156
  publication_venue_name_unified: str | None = Field(
@@ -201,10 +243,25 @@ class ApiError(BaseModel):
201
243
 
202
244
 
203
245
  class SearchPapersResponse(BaseModel):
204
- hits: list[PaperMetadata]
205
- total: int
246
+ results: list[PaperMetadata] = Field(
247
+ ..., description="命中论文列表(后端字段名为 results,非 hits)。"
248
+ )
249
+ total_count: int = Field(
250
+ ...,
251
+ description="命中总数;超过 10000 会被截断为 10000,需精确值时改用深翻页/计数。",
252
+ )
206
253
  page: int
207
254
  page_size: int
255
+ total_pages: int | None = Field(None, description="总页数。")
256
+ next_cursor: str | None = Field(
257
+ None,
258
+ description="深翻页游标,为空表示无更多。page*page_size>10000 时必须改用 cursor(把此值回填到请求的 cursor)。",
259
+ )
260
+ search_time_ms: float | None = Field(None, description="检索耗时(毫秒)。")
261
+ request_tokens: int | None = Field(None, description="输入 query 的 token 数。")
262
+ response_tokens: int | None = Field(
263
+ None, description="输出结果文本字段的 token 数。"
264
+ )
208
265
 
209
266
 
210
267
  class SemanticSearchResponse(BaseModel):
@@ -323,7 +323,7 @@ wheels = [
323
323
 
324
324
  [[package]]
325
325
  name = "sciverse"
326
- version = "0.7.1"
326
+ version = "0.7.2"
327
327
  source = { editable = "." }
328
328
  dependencies = [
329
329
  { name = "httpx" },
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes