askround 0.1.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.
Files changed (81) hide show
  1. askround-0.1.0/LICENSE +201 -0
  2. askround-0.1.0/PKG-INFO +452 -0
  3. askround-0.1.0/README.md +422 -0
  4. askround-0.1.0/plugin/src/askround/__init__.py +3 -0
  5. askround-0.1.0/plugin/src/askround/__main__.py +3 -0
  6. askround-0.1.0/plugin/src/askround/budget.py +108 -0
  7. askround-0.1.0/plugin/src/askround/citations.py +172 -0
  8. askround-0.1.0/plugin/src/askround/claims.py +61 -0
  9. askround-0.1.0/plugin/src/askround/cli.py +1179 -0
  10. askround-0.1.0/plugin/src/askround/costs.py +206 -0
  11. askround-0.1.0/plugin/src/askround/diff.py +170 -0
  12. askround-0.1.0/plugin/src/askround/discover.py +124 -0
  13. askround-0.1.0/plugin/src/askround/doctor.py +65 -0
  14. askround-0.1.0/plugin/src/askround/export.py +113 -0
  15. askround-0.1.0/plugin/src/askround/extract.py +288 -0
  16. askround-0.1.0/plugin/src/askround/gaps.py +117 -0
  17. askround-0.1.0/plugin/src/askround/http.py +94 -0
  18. askround-0.1.0/plugin/src/askround/jobs.py +142 -0
  19. askround-0.1.0/plugin/src/askround/leading.py +71 -0
  20. askround-0.1.0/plugin/src/askround/links.py +153 -0
  21. askround-0.1.0/plugin/src/askround/lookup.py +246 -0
  22. askround-0.1.0/plugin/src/askround/mcp.py +538 -0
  23. askround-0.1.0/plugin/src/askround/mention.py +112 -0
  24. askround-0.1.0/plugin/src/askround/private.py +67 -0
  25. askround-0.1.0/plugin/src/askround/product.py +114 -0
  26. askround-0.1.0/plugin/src/askround/provenance.py +44 -0
  27. askround-0.1.0/plugin/src/askround/providers.py +454 -0
  28. askround-0.1.0/plugin/src/askround/refusals.py +104 -0
  29. askround-0.1.0/plugin/src/askround/report.py +531 -0
  30. askround-0.1.0/plugin/src/askround/research.py +296 -0
  31. askround-0.1.0/plugin/src/askround/reuse.py +96 -0
  32. askround-0.1.0/plugin/src/askround/rivals.py +79 -0
  33. askround-0.1.0/plugin/src/askround/round.py +467 -0
  34. askround-0.1.0/plugin/src/askround/series.py +142 -0
  35. askround-0.1.0/plugin/src/askround/sets.py +122 -0
  36. askround-0.1.0/plugin/src/askround/wiring.py +162 -0
  37. askround-0.1.0/plugin/src/askround.egg-info/PKG-INFO +452 -0
  38. askround-0.1.0/plugin/src/askround.egg-info/SOURCES.txt +79 -0
  39. askround-0.1.0/plugin/src/askround.egg-info/dependency_links.txt +1 -0
  40. askround-0.1.0/plugin/src/askround.egg-info/entry_points.txt +2 -0
  41. askround-0.1.0/plugin/src/askround.egg-info/requires.txt +4 -0
  42. askround-0.1.0/plugin/src/askround.egg-info/top_level.txt +1 -0
  43. askround-0.1.0/pyproject.toml +94 -0
  44. askround-0.1.0/setup.cfg +4 -0
  45. askround-0.1.0/tests/test_brief.py +110 -0
  46. askround-0.1.0/tests/test_budget.py +107 -0
  47. askround-0.1.0/tests/test_citations.py +137 -0
  48. askround-0.1.0/tests/test_claims.py +117 -0
  49. askround-0.1.0/tests/test_cli.py +127 -0
  50. askround-0.1.0/tests/test_contract.py +164 -0
  51. askround-0.1.0/tests/test_costs.py +101 -0
  52. askround-0.1.0/tests/test_dependencies.py +42 -0
  53. askround-0.1.0/tests/test_diff.py +100 -0
  54. askround-0.1.0/tests/test_discover.py +120 -0
  55. askround-0.1.0/tests/test_doctor.py +76 -0
  56. askround-0.1.0/tests/test_export.py +94 -0
  57. askround-0.1.0/tests/test_extract.py +192 -0
  58. askround-0.1.0/tests/test_gaps.py +178 -0
  59. askround-0.1.0/tests/test_jobs.py +129 -0
  60. askround-0.1.0/tests/test_leading.py +103 -0
  61. askround-0.1.0/tests/test_links.py +120 -0
  62. askround-0.1.0/tests/test_live.py +39 -0
  63. askround-0.1.0/tests/test_locale.py +111 -0
  64. askround-0.1.0/tests/test_lookup.py +172 -0
  65. askround-0.1.0/tests/test_mcp.py +138 -0
  66. askround-0.1.0/tests/test_mention.py +65 -0
  67. askround-0.1.0/tests/test_private.py +78 -0
  68. askround-0.1.0/tests/test_providers.py +129 -0
  69. askround-0.1.0/tests/test_recorded.py +72 -0
  70. askround-0.1.0/tests/test_refusals.py +103 -0
  71. askround-0.1.0/tests/test_report.py +119 -0
  72. askround-0.1.0/tests/test_research.py +206 -0
  73. askround-0.1.0/tests/test_retries.py +112 -0
  74. askround-0.1.0/tests/test_reuse.py +107 -0
  75. askround-0.1.0/tests/test_rivals.py +91 -0
  76. askround-0.1.0/tests/test_round.py +199 -0
  77. askround-0.1.0/tests/test_series.py +124 -0
  78. askround-0.1.0/tests/test_sets.py +94 -0
  79. askround-0.1.0/tests/test_site.py +96 -0
  80. askround-0.1.0/tests/test_tokens.py +127 -0
  81. askround-0.1.0/tests/test_wiring.py +133 -0
askround-0.1.0/LICENSE ADDED
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright 2026 Alexandre Oliveira
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
@@ -0,0 +1,452 @@
1
+ Metadata-Version: 2.4
2
+ Name: askround
3
+ Version: 0.1.0
4
+ Summary: See what ChatGPT, Gemini, Claude and Perplexity recommend: ask them all the same question with web search and read whether your product is named, where, beside which rivals, and what they claim about it.
5
+ Author: Alexandre Oliveira
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/alegauss/askround
8
+ Project-URL: Repository, https://github.com/alegauss/askround
9
+ Project-URL: Issues, https://github.com/alegauss/askround/issues
10
+ Project-URL: Changelog, https://github.com/alegauss/askround/blob/main/docs/CHANGELOG.md
11
+ Keywords: ai-visibility,llm-visibility,generative-engine-optimization,chatgpt,gemini,claude,perplexity,mcp,cli
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3 :: Only
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
22
+ Classifier: Topic :: Utilities
23
+ Requires-Python: >=3.11
24
+ Description-Content-Type: text/markdown
25
+ License-File: LICENSE
26
+ Provides-Extra: dev
27
+ Requires-Dist: pytest>=8; extra == "dev"
28
+ Requires-Dist: ruff>=0.6; extra == "dev"
29
+ Dynamic: license-file
30
+
31
+ # askround
32
+
33
+ **Site:** [alegauss.com/askround](https://alegauss.com/askround/), with a real
34
+ recorded round. **Docs:** [install, a first session, prompt sets, budget, reports, the MCP
35
+ tools and the CLI](https://alegauss.com/askround/docs/).
36
+
37
+ People now ask ChatGPT or Gemini which app to use, and you cannot see the answer they
38
+ get. askround asks the assistants for you: the same question to OpenAI, Gemini,
39
+ Anthropic and Perplexity, with web search on, several times each. It tells you whether
40
+ your product is named and in which position, which rivals sit around it, what the
41
+ answers claim about it, and which of your pages they cite.
42
+
43
+ A real run, from 12/11/2026: four questions about Modelado, a Windows photo-to-3D app,
44
+ one run each on two providers (`askround run examples/modelado.toml --runs 1`):
45
+
46
+ | # | question | purpose | openai | gemini |
47
+ |---|---|---|---|---|
48
+ | 1 | What is a free offline app to turn a photo into a 3D model on Windows? | generic | 0/1 | 0/1 |
49
+ | 2 | What app can turn a photo into a 3D model on Windows without an NVIDIA GPU (AMD or CPU only)? | constraint | 1/1 | 0/1 |
50
+ | 3 | What Windows app can turn a photo into a relief STL for 3D printing? | use case | 0/1 | 0/1 |
51
+ | 4 | Qual app de Windows transforma uma foto em modelo 3D usando só a CPU, sem placa NVIDIA? | language | 1/1, #1 | 0/1 |
52
+
53
+ Each cell is the number of runs that named the app, then its position. ChatGPT finds
54
+ Modelado when the asker has no NVIDIA card, and Gemini doesn't find it at all. That is
55
+ who the app is for, and what its first screen has to say.
56
+
57
+ **askround measures the APIs, not the apps.** An API answer with web search is close to
58
+ what the chat apps say but not the same page: the apps add memory, personalisation and
59
+ their own choice of model. askround records what produced each answer (the model asked
60
+ for and the one that answered, the search tool, the settings, the time), and every
61
+ report is about the API.
62
+
63
+ ```
64
+ pip install askround # from a checkout until the first release: pip install .
65
+ askround doctor # which providers have a key
66
+ askround ask "What app can turn a photo into a 3D model on Windows without an NVIDIA GPU?" \
67
+ --product "Modelado,alegauss.com/modelado" --extract
68
+ ```
69
+
70
+ Python 3.11 or later and the standard library only, so nothing in it is specific to one
71
+ system; it is tested on Windows.
72
+
73
+ Every provider whose API key is in the environment is asked in parallel:
74
+ `OPENAI_API_KEY`, `GEMINI_API_KEY` (or `GOOGLE_API_KEY`), `ANTHROPIC_API_KEY` and
75
+ `PERPLEXITY_API_KEY`. askround reads the keys at call time and never stores them.
76
+ `askround doctor` lists which providers can run here: the key variable (never its
77
+ value), the model, the search tool and a free check call; `--no-check` makes no call.
78
+
79
+ Web search is on, through each provider's own search tool, because that is how
80
+ assistants find and recommend products. Each answer is printed with the sources it
81
+ cited. `--no-search` asks the same question without search, to compare.
82
+
83
+ Each round is saved under `.askround/rounds/<date>/<time>-<question>/` (change it with
84
+ `--out` or `ASKROUND_DIR`): `round.json` holds the question and each provider's answer,
85
+ search queries and sources, and `<provider>-<run>.json` the raw response as the provider
86
+ sent it. Assistants answer differently from one run to the next, so a product is
87
+ reported as "named in 2/3 runs, positions #1, #3, -", never from a single answer.
88
+
89
+ | Option | What it does |
90
+ | ------------------- | --------------------------------------------------- |
91
+ | `--provider NAME` | ask only this provider (`openai`, `gemini`, `anthropic`, `perplexity`), repeatable |
92
+ | `--model PROVIDER=MODEL` | pin the model one provider uses, e.g. `openai=gpt-5-2025-08-07`, repeatable |
93
+ | `--no-search` | ask without web search |
94
+ | `--runs N` | ask each provider N times, in fresh conversations (default 3) |
95
+ | `--product NAME[,ALIAS...]` | report how many runs named the product and its position in each, repeatable |
96
+ | `--extract [PROVIDER]` | read each answer's recommendations in order with a model (one extra call per answer), so "Best fit" in a table or "my first choice" in prose ranks the product first |
97
+ | `--locale COUNTRY[:LANG]` | ask as someone in this country, e.g. `BR:pt-BR`; repeat to compare countries side by side |
98
+ | `--out DIR` | where the round is saved |
99
+ | `--json` | print the round as JSON |
100
+
101
+ Without `--extract`, a product's position is read from the answer's text: its place
102
+ among the top-level list items, numbered headings and table rows. With it, a small model
103
+ (gpt-5-mini, gemini-2.5-flash or claude-haiku-5-5, whichever has a key; override with
104
+ `ASKROUND_EXTRACT_MODEL`) lists what the answer recommends in its own order, each with a
105
+ quote. The text stays the check: a product counts as named only if its name is in the
106
+ answer, and a quote not found in the answer is marked `(?)`. Positions ranked this way
107
+ show a `*`.
108
+
109
+ Give the product's own places among its names, anything with a dot: a domain or a URL
110
+ prefix (`--product "Modelado,alegauss.com/modelado,github.com/alegauss/modelado"`).
111
+ The report then says, per provider, how many answers cited one of its pages and which
112
+ kind (site, repository, store, docs), and `round.json` lists every page and the
113
+ third-party domains cited. Tracking parameters are dropped; Gemini's redirect links are
114
+ counted by the domain in their title.
115
+
116
+ `--check-links` follows every link an answer writes beside the product's name (in its
117
+ table row or list item, or close to it in prose), images included, and flags those that
118
+ land on someone else's page, such as another app's Store page shown for yours, or that
119
+ are broken. A page counts as the product's when it is on one of its places or its
120
+ title names it.
121
+
122
+ With `--extract`, the report also lists what the answers claim about the product, from
123
+ tables and prose: `CPU only: Check build`, `Cost: Free, open-source`, each with the
124
+ providers that said it and the passage it came from. A claim whose quote is not in the
125
+ answer is counted apart and never shown as a finding.
126
+
127
+ With `--extract`, the report also lists the rivals: every other product the answers
128
+ recommend, with how many answers named it, its rank in each and which providers did
129
+ (the first ten in the report, all of them in `round.json`, each with the quote that
130
+ recommends it).
131
+
132
+ Each API takes a different part of a locale, and only for its search: OpenAI,
133
+ Anthropic and Perplexity an approximate country, Gemini a language. What a provider
134
+ cannot take is recorded on the answer and shown as "ignored by the API". Write the
135
+ question in the language you are testing: that is still the strongest signal.
136
+
137
+ A rate limit, a server error, a timeout or a dropped connection is retried up to three
138
+ times with backoff (honouring `Retry-After`); a rejected key or a missing model is not.
139
+ An answer that still fails is recorded with its error, the rest of the round carries
140
+ on, and the report lists the failures at the end.
141
+
142
+ The model per provider is overridden with `ASKROUND_OPENAI_MODEL`,
143
+ `ASKROUND_GEMINI_MODEL`, `ASKROUND_ANTHROPIC_MODEL` and `ASKROUND_PERPLEXITY_MODEL`.
144
+
145
+ ## A product file, and the facts the answers get wrong
146
+
147
+ ```toml
148
+ # modelado.toml
149
+ [product]
150
+ name = "Modelado"
151
+ places = ["alegauss.com/modelado", "github.com/alegauss/modelado"]
152
+
153
+ [[fact]]
154
+ attribute = "Runs without an NVIDIA GPU"
155
+ value = "Yes: any DirectX 12 GPU through DirectML, or the CPU"
156
+ page = "https://alegauss.com/modelado/" # where it should be said plainly
157
+ ```
158
+
159
+ `askround ask "..." --product-file modelado.toml --extract` asks about the product the
160
+ file describes and compares what the answers claim with its facts, in one more call: a
161
+ claim that contradicts a fact, or doubts it ('CPU only: Check build'), is reported as a
162
+ fact gap with the page that should state the fact plainly and the product's pages the
163
+ answers did cite.
164
+
165
+ ## What a round costs
166
+
167
+ Before a round, askround prints an estimate from the number of calls, the tokens a
168
+ searched answer usually takes and each provider's prices, and refuses a round above the
169
+ cap you set (`--max-cost 0.50`, or `ASKROUND_MAX_COST`) before anything is sent. After
170
+ it, the report gives the actual cost per provider from the usage each API reported,
171
+ the extraction pass included.
172
+
173
+ The prices are a dated table in `plugin/src/askround/costs.py`: Anthropic's from its model table
174
+ of 2026-11-03, the others the last known list prices, to be checked against the
175
+ providers' pages. `ASKROUND_PRICES` names a JSON file that replaces any of them:
176
+ `{"models": {"gpt-5": {"input": 1.25, "output": 10.0}}, "search": {"openai": 0.01}}`.
177
+
178
+ ### A budget an agent cannot raise
179
+
180
+ Set what askround may spend in its config file, `%APPDATA%\askround\config.toml` on
181
+ Windows, `~/.config/askround/config.toml` elsewhere (or the file `ASKROUND_CONFIG`
182
+ names):
183
+
184
+ ```toml
185
+ [budget]
186
+ day = 1.00
187
+ month = 10.00
188
+ ```
189
+
190
+ Every saved round adds its actual cost to `spent.jsonl` beside the rounds. A run whose
191
+ estimate would pass the day's or the month's budget is refused before any call
192
+ (`budget.over`): it waits for you. You approve it by running it with `--approve`, or
193
+ raise the budget; an agent using the MCP tools cannot do either, and every paid result
194
+ it gets says what has been spent against the budget. `askround budget` shows it.
195
+
196
+ ### The same question twice costs once
197
+
198
+ A question asked again the same way within a day (the same text, providers, models,
199
+ locales and search, as many runs, extraction if wanted) returns the stored round, marked
200
+ with its age, and calls no provider. A round where some answers failed is asked again.
201
+ `--fresh` (or `fresh: true` over MCP) asks anew; the window is set in the config file:
202
+
203
+ ```toml
204
+ [reuse]
205
+ max_age_hours = 24 # 0 turns reuse off
206
+ ```
207
+
208
+ ## Prompt sets: the same questions every week
209
+
210
+ Add `[[question]]` entries to the product file and it becomes a prompt set:
211
+
212
+ ```toml
213
+ [[question]]
214
+ text = "What app can turn a photo into a 3D model on Windows without an NVIDIA GPU?"
215
+ purpose = "constraint" # generic, constraint, use case, language
216
+ locale = "BR:pt-BR" # optional
217
+ runs = 3 # optional
218
+ ```
219
+
220
+ A question that names the product it tests (its name, an alias, one of its places, or
221
+ a phrase listed under `phrases`) measures nothing, so it is refused before anything is
222
+ paid for. A question that asks about the product on purpose is marked `brand = true`
223
+ (or `--brand` on `ask`); it runs, and is reported apart.
224
+
225
+ A round sends the same text to every provider, so a question that carries personal data
226
+ (an email address, a phone number, a CPF, an IP address, an API key, or a name on your
227
+ own list in `ASKROUND_PRIVATE` or the file `ASKROUND_PRIVATE_FILE` names) stops before
228
+ anything is sent, naming the question and what it found.
229
+
230
+ `askround run modelado.toml` asks every question as its own round, kept together in
231
+ one set directory, and prints one table of question by provider before each question's
232
+ report. `askround report` on the set's directory prints it again.
233
+ [`examples/modelado.toml`](examples/modelado.toml) is the set this tool was built from.
234
+
235
+ ## Which question leads to the product?
236
+
237
+ ```
238
+ askround discover modelado.toml --seed "free offline app photo to 3D model on Windows"
239
+ ```
240
+
241
+ From the seed and the product file's facts, a model writes variants (a constraint the
242
+ product meets, a use case, another language) that never name the product; askround
243
+ runs them with the seed and ranks them by how often and how high the product appears.
244
+ It runs once per question by default, which is a sketch: add the promising questions
245
+ to the product file and `askround run` them with more runs.
246
+
247
+ ## Does a recommended product exist?
248
+
249
+ ```
250
+ askround lookup "MeshForge3D" "Modly" --context "a Windows photo-to-3D app"
251
+ askround ask "..." --extract --check-rivals # the top 10 rivals of a round
252
+ ```
253
+
254
+ Each name is looked up on GitHub first (free; a repository of that name with at least
255
+ 10 stars), then with one searched call on OpenAI, Anthropic or Gemini, which answers
256
+ found, ambiguous or not found with the page. A "found" whose page is not among the
257
+ pages that search returned is downgraded to ambiguous. `--no-web` keeps it to GitHub.
258
+ The report marks each checked rival, and names the ones found nowhere.
259
+
260
+ ## The series
261
+
262
+ Every round saved also appends one line per answer to `series.jsonl` beside the rounds:
263
+ when, which question and set, which provider and model, and what was read (each
264
+ tracked product's mention and position, the recommendations, the cited pages, the
265
+ claims). It is an index of the rounds, so `--rebuild` writes it again from them.
266
+
267
+ ```
268
+ askround series --product Modelado # answers naming it, per week
269
+ askround series --product Modelado --by day --question nvidia
270
+ ```
271
+
272
+ `askround diff` compares the latest two runs of the same questions (or two you name)
273
+ and lists only what changed, per question and provider: the product's share and best
274
+ position, rivals that appeared or left, its pages newly cited or no longer cited, and
275
+ claims made or dropped.
276
+
277
+ ## Export, and a weekly round
278
+
279
+ `askround export --product Modelado` writes the product's numbers from the series, one
280
+ row per day, provider and question (answers, named, frequency, best and mean position,
281
+ rivals, its pages cited), as CSV, JSON, or with `--format daily` in the shape a
282
+ `daily.json` of numbers per product takes (`{product: {day: {"assistants": {...}}}}`).
283
+ `--from`/`--to` bound the days and `--file` writes it out.
284
+
285
+ askround does not schedule itself; the system does. Every Monday at 08:00, on Windows:
286
+
287
+ ```
288
+ schtasks /Create /SC WEEKLY /D MON /ST 08:00 /TN askround-modelado ^
289
+ /TR "cmd /c cd /d D:\work && askround run modelado.toml --extract --max-cost 2 && askround export --product Modelado --format daily --file modelado-assistants.json"
290
+ ```
291
+
292
+ and with cron:
293
+
294
+ ```
295
+ 0 8 * * 1 cd ~/work && askround run modelado.toml --extract --max-cost 2 && askround export --product Modelado --format daily --file modelado-assistants.json
296
+ ```
297
+
298
+ The keys come from the environment the task runs in; `--max-cost` keeps an unattended
299
+ run from spending more than you agreed to.
300
+
301
+ ## The report
302
+
303
+ ```
304
+ askround report # the latest round, as Markdown
305
+ askround report <round dir> --json # one round, as JSON for a pipeline
306
+ askround report --product Blender # the same round, read for another product
307
+ ```
308
+
309
+ `report` reads a stored round back and sums it up: for each product, a table of
310
+ provider by share of runs and positions, what the answers claim about it, which of its
311
+ pages they cite, then the rivals. The JSON holds the same facts and never the answers'
312
+ text, which stays in `round.json`.
313
+
314
+ ## Research mode
315
+
316
+ ```
317
+ askround research "Which open-source image-to-3D models allow commercial use?"
318
+ ```
319
+
320
+ asks every provider once with search, reads each answer for the factual statements it
321
+ makes (each with a quote found in the answer), merges the statements that say the same
322
+ thing, and lists the claims by how many providers support them, each with every
323
+ provider's passage and the sources, those naming the claim's subject first and primary
324
+ ones (repositories, licence texts, papers, official docs) before secondary ones.
325
+ Support is agreement among assistants, not proof: check a claim at its primary source.
326
+
327
+ Where the assistants disagree on a fact (one says a model's licence allows commercial
328
+ use, another that it does not), the report opens with it: both sides, their passages,
329
+ and the primary sources to read to settle it.
330
+
331
+ ## For agents: the plugin and the MCP server
332
+
333
+ In Claude Code, the plugin installs the MCP server, four slash commands (`/askround:ask`,
334
+ `/askround:run`, `/askround:report`, `/askround:diff`) and a skill that tells the agent to
335
+ run a question across assistants itself, instead of asking you to paste ChatGPT's
336
+ answer. The plugin carries the package (`plugin/src/askround`) and runs it with the
337
+ Python on the machine, so nothing else is installed and nothing has to be on the PATH:
338
+
339
+ ```
340
+ /plugin marketplace add alegauss/askround
341
+ /plugin install askround@askround
342
+ ```
343
+
344
+ To wire a project for everyone who clones it, run `askround install` in it (or
345
+ `python plugin/scripts/askround.py install <project>` from a checkout) and commit
346
+ `.claude/settings.json`: it names the marketplace and enables the plugin, keeping every
347
+ other setting, so each clone is offered the plugin on its first session.
348
+ `askround install --check` writes nothing and exits 1 when the entries are missing, for
349
+ CI. Claude Code on the web installs no plugin, so `askround install --vendor` also copies
350
+ askround into `.claude/askround/` with a launcher and names it in the project's
351
+ `.mcp.json`; commit both, and `--vendor --check` says when the copy is behind. The server
352
+ alone: `claude mcp add askround -- askround mcp`. Either serves:
353
+
354
+ | Tool | What it does |
355
+ | --- | --- |
356
+ | `askround_ask` | ask a question across providers; returns the report and lists the answers |
357
+ | `askround_answer` | the full text of one answer, with its sources |
358
+ | `askround_run` | run a prompt set (a product file); returns the set's report |
359
+ | `askround_research` | a research question everywhere, its claims merged by support |
360
+ | `askround_job` | a round started with `background: true`: its progress, then its result |
361
+ | `askround_report` | the report of a stored round or set |
362
+ | `askround_diff` | what changed between two runs of the same questions |
363
+ | `askround_doctor` | which providers can run (key variable names, never values) |
364
+
365
+ The same refusals hold for an agent: personal data, a question naming its product, and
366
+ a run estimated above `max_cost` (or `ASKROUND_MAX_COST`) stop before anything is
367
+ paid for. The tool schemas are written for the project the server runs in: only the
368
+ providers with a key, the product files found at its top or one directory down (with
369
+ their question counts) under `askround_run`'s `file`, and your day and month budget, with
370
+ what is already spent, beside `max_cost`.
371
+
372
+ `askround_ask` returns a brief: a few hundred tokens for a whole round (a real 2x2 round
373
+ with extraction: 614 characters, against 12,253 of answers). Each fact in it carries the
374
+ ids of the answers it came from (`openai#2`, `openai@BR#2`); `askround_answer` with an
375
+ id returns that answer, or with `around: "<quote>"` only the passage behind the fact.
376
+ `detail: true` returns the full report. On the command line: `askround report --brief`.
377
+ `askround_run` returns one brief per question in the set, and `detail: true` the set's
378
+ full report.
379
+
380
+ What each response costs the agent, measured on the recorded rounds in `tests/fixtures`
381
+ (two providers, three runs, extraction on; one token per four characters of JSON, so
382
+ an approximation, the same way for every tool):
383
+
384
+ | Response | Tokens | Budget |
385
+ | --- | ---: | ---: |
386
+ | `askround_ask` (brief) | 304 | 400 |
387
+ | `askround_ask` with `detail: true` | 1,360 | 1,800 |
388
+ | `askround_answer` | 868 | 1,100 |
389
+ | `askround_answer` with `around` | 175 | 250 |
390
+ | `askround_research` | 163 | 250 |
391
+ | `askround_run` (one question per purpose) | 760 | 1,000 |
392
+ | `askround_report` | 1,136 | 1,500 |
393
+ | `askround_diff` | 112 | 200 |
394
+ | `askround_doctor` | 257 | 350 |
395
+
396
+ The budgets live in `budgets/mcp-tokens.json` and `tests/test_tokens.py` fails a response
397
+ over its budget, so a field added to a response is a reviewed change. `pytest -s -k
398
+ figures` prints the table again.
399
+
400
+ A round takes minutes. With `background: true`, `askround_ask` and `askround_run` check
401
+ and estimate it, start it in a process of its own and return a job id at once; the
402
+ agent keeps working and reads it with `askround_job`: running (with how many answers
403
+ are in), done (with its brief), refused, failed, or interrupted. Each answer is written
404
+ the moment it lands, so an interrupted job keeps every answer it finished. The jobs
405
+ live in `jobs/` beside the rounds; `--estimate-only` on the command line runs the same
406
+ checks and prints the estimate without asking anything.
407
+
408
+ ## Refused before it is paid for
409
+
410
+ Every check that needs no call runs before the first one, and a refusal lists every
411
+ problem at once, each with a code and the change that passes it:
412
+
413
+ ```
414
+ askround: refused before anything was sent: 2 problem(s)
415
+ [question.leading] the question names the product it tests (Modelado): 'Is Modelado good?'
416
+ fix: Reword it as a person who does not know the product would ask; or ...
417
+ [cost.over-cap] the estimate $0.2972 is above the cap $0.0010
418
+ fix: Ask fewer providers or fewer runs, drop --extract, or raise the cap ...
419
+ ```
420
+
421
+ `askround explain <code>` says what a code means; `askround explain` lists them. For an
422
+ agent, the MCP schemas carry the same limits (a question's length, the providers that
423
+ have a key), so a call that would be refused fails at the schema.
424
+
425
+ ## The output contract
426
+
427
+ Every command takes `--json` (`export` takes `--format json`). Each JSON object names
428
+ its schema, `"schema": "askround/round@1"`, and the schemas are published in
429
+ [`schema/`](schema/) as JSON Schema. In a round, every fact says where it came from: the
430
+ answer's id (`openai#2`, `openai@BR#2`) and the character range of its passage in that
431
+ answer's text (`"range": [412, 437]`, end excluded), so a script or an agent can cite it.
432
+
433
+ | Exit code | Means |
434
+ | --- | --- |
435
+ | 0 | every answer came back |
436
+ | 3 | a partial round: it finished, some answers failed (they are listed) |
437
+ | 1 | no answer came back |
438
+ | 2 | refused before anything was sent (personal data, a leading question, over the cost cap, no key) or a usage error |
439
+
440
+ ## Development
441
+
442
+ ```
443
+ pip install -e .[dev]
444
+ python -m pytest # no key and no network: a test that calls out fails
445
+ python -m ruff check .
446
+ python -m ruff format --check .
447
+ ASKROUND_LIVE=1 python -m pytest -m live # each adapter with a key, against its real API
448
+ ```
449
+
450
+ The adapters are tested against real responses recorded from a live round
451
+ (`tests/fixtures/`, ids scrubbed), so a provider changing its shape shows up as a
452
+ failing recording rather than a silent miss.