Upload folder using huggingface_hub
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- .gitattributes +3 -0
- CMakeLists.txt +23 -0
- LICENSE +202 -0
- MANIFEST.in +1 -0
- README.md +595 -3
- USAGE_POLICY +1 -0
- _build/gpt_oss_build_backend/__init__.py +1 -0
- _build/gpt_oss_build_backend/backend.py +140 -0
- awesome-gpt-oss.md +90 -0
- compatibility-test/.gitignore +142 -0
- compatibility-test/README.md +29 -0
- compatibility-test/analysis.ts +142 -0
- compatibility-test/cases.jsonl +30 -0
- compatibility-test/index.ts +196 -0
- compatibility-test/package-lock.json +1633 -0
- compatibility-test/package.json +11 -0
- compatibility-test/providers.ts +15 -0
- compatibility-test/runCase.ts +331 -0
- compatibility-test/tools.ts +156 -0
- docs/gpt-oss-120b.svg +3 -0
- docs/gpt-oss-20b.svg +3 -0
- docs/gpt-oss.svg +3 -0
- examples/agents-sdk-js/index.ts +90 -0
- examples/agents-sdk-js/package-lock.json +1798 -0
- examples/agents-sdk-js/package.json +20 -0
- examples/agents-sdk-python/example.py +102 -0
- examples/agents-sdk-python/pyproject.toml +9 -0
- examples/gradio/gradio_chat.py +247 -0
- examples/streamlit/streamlit_chat.py +354 -0
- gpt-oss-mcp-server/README.md +29 -0
- gpt-oss-mcp-server/browser_server.py +120 -0
- gpt-oss-mcp-server/build-system-prompt.py +116 -0
- gpt-oss-mcp-server/pyproject.toml +8 -0
- gpt-oss-mcp-server/python_server.py +33 -0
- gpt-oss-mcp-server/reference-system-prompt.py +46 -0
- gpt_oss/__init__.py +0 -0
- gpt_oss/chat.py +369 -0
- gpt_oss/evals/README.md +4 -0
- gpt_oss/evals/__init__.py +0 -0
- gpt_oss/evals/__main__.py +211 -0
- gpt_oss/evals/abcd_grader.py +121 -0
- gpt_oss/evals/aime_eval.py +97 -0
- gpt_oss/evals/basic_eval.py +38 -0
- gpt_oss/evals/chat_completions_sampler.py +93 -0
- gpt_oss/evals/gpqa_eval.py +125 -0
- gpt_oss/evals/healthbench_eval.py +612 -0
- gpt_oss/evals/report.py +207 -0
- gpt_oss/evals/responses_sampler.py +85 -0
- gpt_oss/evals/types.py +66 -0
- gpt_oss/generate.py +95 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,6 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
docs/gpt-oss-120b.svg filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
docs/gpt-oss-20b.svg filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
docs/gpt-oss.svg filter=lfs diff=lfs merge=lfs -text
|
CMakeLists.txt
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
cmake_minimum_required(VERSION 3.26)
|
| 2 |
+
project(gpt_oss LANGUAGES C CXX)
|
| 3 |
+
|
| 4 |
+
# If not defined externally, auto-detect
|
| 5 |
+
if(NOT DEFINED GPTOSS_BUILD_METAL)
|
| 6 |
+
if(APPLE AND CMAKE_SYSTEM_PROCESSOR MATCHES "arm64")
|
| 7 |
+
message(STATUS "Apple Silicon detected → enabling GPTOSS_BUILD_METAL")
|
| 8 |
+
set(GPTOSS_BUILD_METAL ON)
|
| 9 |
+
else()
|
| 10 |
+
message(STATUS "Non-Apple Silicon → disabling GPTOSS_BUILD_METAL")
|
| 11 |
+
set(GPTOSS_BUILD_METAL OFF)
|
| 12 |
+
endif()
|
| 13 |
+
else()
|
| 14 |
+
message(STATUS "GPTOSS_BUILD_METAL manually set to: ${GPTOSS_BUILD_METAL}")
|
| 15 |
+
endif()
|
| 16 |
+
|
| 17 |
+
# Now declare it as a cache variable (respects user-provided value)
|
| 18 |
+
set(GPTOSS_BUILD_METAL "${GPTOSS_BUILD_METAL}" CACHE BOOL "Enable Metal backend")
|
| 19 |
+
|
| 20 |
+
if(GPTOSS_BUILD_METAL)
|
| 21 |
+
enable_language(OBJC)
|
| 22 |
+
add_subdirectory(gpt_oss/metal)
|
| 23 |
+
endif()
|
LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
|
| 2 |
+
Apache License
|
| 3 |
+
Version 2.0, January 2004
|
| 4 |
+
http://www.apache.org/licenses/
|
| 5 |
+
|
| 6 |
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
| 7 |
+
|
| 8 |
+
1. Definitions.
|
| 9 |
+
|
| 10 |
+
"License" shall mean the terms and conditions for use, reproduction,
|
| 11 |
+
and distribution as defined by Sections 1 through 9 of this document.
|
| 12 |
+
|
| 13 |
+
"Licensor" shall mean the copyright owner or entity authorized by
|
| 14 |
+
the copyright owner that is granting the License.
|
| 15 |
+
|
| 16 |
+
"Legal Entity" shall mean the union of the acting entity and all
|
| 17 |
+
other entities that control, are controlled by, or are under common
|
| 18 |
+
control with that entity. For the purposes of this definition,
|
| 19 |
+
"control" means (i) the power, direct or indirect, to cause the
|
| 20 |
+
direction or management of such entity, whether by contract or
|
| 21 |
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
| 22 |
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
| 23 |
+
|
| 24 |
+
"You" (or "Your") shall mean an individual or Legal Entity
|
| 25 |
+
exercising permissions granted by this License.
|
| 26 |
+
|
| 27 |
+
"Source" form shall mean the preferred form for making modifications,
|
| 28 |
+
including but not limited to software source code, documentation
|
| 29 |
+
source, and configuration files.
|
| 30 |
+
|
| 31 |
+
"Object" form shall mean any form resulting from mechanical
|
| 32 |
+
transformation or translation of a Source form, including but
|
| 33 |
+
not limited to compiled object code, generated documentation,
|
| 34 |
+
and conversions to other media types.
|
| 35 |
+
|
| 36 |
+
"Work" shall mean the work of authorship, whether in Source or
|
| 37 |
+
Object form, made available under the License, as indicated by a
|
| 38 |
+
copyright notice that is included in or attached to the work
|
| 39 |
+
(an example is provided in the Appendix below).
|
| 40 |
+
|
| 41 |
+
"Derivative Works" shall mean any work, whether in Source or Object
|
| 42 |
+
form, that is based on (or derived from) the Work and for which the
|
| 43 |
+
editorial revisions, annotations, elaborations, or other modifications
|
| 44 |
+
represent, as a whole, an original work of authorship. For the purposes
|
| 45 |
+
of this License, Derivative Works shall not include works that remain
|
| 46 |
+
separable from, or merely link (or bind by name) to the interfaces of,
|
| 47 |
+
the Work and Derivative Works thereof.
|
| 48 |
+
|
| 49 |
+
"Contribution" shall mean any work of authorship, including
|
| 50 |
+
the original version of the Work and any modifications or additions
|
| 51 |
+
to that Work or Derivative Works thereof, that is intentionally
|
| 52 |
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
| 53 |
+
or by an individual or Legal Entity authorized to submit on behalf of
|
| 54 |
+
the copyright owner. For the purposes of this definition, "submitted"
|
| 55 |
+
means any form of electronic, verbal, or written communication sent
|
| 56 |
+
to the Licensor or its representatives, including but not limited to
|
| 57 |
+
communication on electronic mailing lists, source code control systems,
|
| 58 |
+
and issue tracking systems that are managed by, or on behalf of, the
|
| 59 |
+
Licensor for the purpose of discussing and improving the Work, but
|
| 60 |
+
excluding communication that is conspicuously marked or otherwise
|
| 61 |
+
designated in writing by the copyright owner as "Not a Contribution."
|
| 62 |
+
|
| 63 |
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
| 64 |
+
on behalf of whom a Contribution has been received by Licensor and
|
| 65 |
+
subsequently incorporated within the Work.
|
| 66 |
+
|
| 67 |
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
| 68 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 69 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 70 |
+
copyright license to reproduce, prepare Derivative Works of,
|
| 71 |
+
publicly display, publicly perform, sublicense, and distribute the
|
| 72 |
+
Work and such Derivative Works in Source or Object form.
|
| 73 |
+
|
| 74 |
+
3. Grant of Patent License. Subject to the terms and conditions of
|
| 75 |
+
this License, each Contributor hereby grants to You a perpetual,
|
| 76 |
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
| 77 |
+
(except as stated in this section) patent license to make, have made,
|
| 78 |
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
| 79 |
+
where such license applies only to those patent claims licensable
|
| 80 |
+
by such Contributor that are necessarily infringed by their
|
| 81 |
+
Contribution(s) alone or by combination of their Contribution(s)
|
| 82 |
+
with the Work to which such Contribution(s) was submitted. If You
|
| 83 |
+
institute patent litigation against any entity (including a
|
| 84 |
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
| 85 |
+
or a Contribution incorporated within the Work constitutes direct
|
| 86 |
+
or contributory patent infringement, then any patent licenses
|
| 87 |
+
granted to You under this License for that Work shall terminate
|
| 88 |
+
as of the date such litigation is filed.
|
| 89 |
+
|
| 90 |
+
4. Redistribution. You may reproduce and distribute copies of the
|
| 91 |
+
Work or Derivative Works thereof in any medium, with or without
|
| 92 |
+
modifications, and in Source or Object form, provided that You
|
| 93 |
+
meet the following conditions:
|
| 94 |
+
|
| 95 |
+
(a) You must give any other recipients of the Work or
|
| 96 |
+
Derivative Works a copy of this License; and
|
| 97 |
+
|
| 98 |
+
(b) You must cause any modified files to carry prominent notices
|
| 99 |
+
stating that You changed the files; and
|
| 100 |
+
|
| 101 |
+
(c) You must retain, in the Source form of any Derivative Works
|
| 102 |
+
that You distribute, all copyright, patent, trademark, and
|
| 103 |
+
attribution notices from the Source form of the Work,
|
| 104 |
+
excluding those notices that do not pertain to any part of
|
| 105 |
+
the Derivative Works; and
|
| 106 |
+
|
| 107 |
+
(d) If the Work includes a "NOTICE" text file as part of its
|
| 108 |
+
distribution, then any Derivative Works that You distribute must
|
| 109 |
+
include a readable copy of the attribution notices contained
|
| 110 |
+
within such NOTICE file, excluding those notices that do not
|
| 111 |
+
pertain to any part of the Derivative Works, in at least one
|
| 112 |
+
of the following places: within a NOTICE text file distributed
|
| 113 |
+
as part of the Derivative Works; within the Source form or
|
| 114 |
+
documentation, if provided along with the Derivative Works; or,
|
| 115 |
+
within a display generated by the Derivative Works, if and
|
| 116 |
+
wherever such third-party notices normally appear. The contents
|
| 117 |
+
of the NOTICE file are for informational purposes only and
|
| 118 |
+
do not modify the License. You may add Your own attribution
|
| 119 |
+
notices within Derivative Works that You distribute, alongside
|
| 120 |
+
or as an addendum to the NOTICE text from the Work, provided
|
| 121 |
+
that such additional attribution notices cannot be construed
|
| 122 |
+
as modifying the License.
|
| 123 |
+
|
| 124 |
+
You may add Your own copyright statement to Your modifications and
|
| 125 |
+
may provide additional or different license terms and conditions
|
| 126 |
+
for use, reproduction, or distribution of Your modifications, or
|
| 127 |
+
for any such Derivative Works as a whole, provided Your use,
|
| 128 |
+
reproduction, and distribution of the Work otherwise complies with
|
| 129 |
+
the conditions stated in this License.
|
| 130 |
+
|
| 131 |
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
| 132 |
+
any Contribution intentionally submitted for inclusion in the Work
|
| 133 |
+
by You to the Licensor shall be under the terms and conditions of
|
| 134 |
+
this License, without any additional terms or conditions.
|
| 135 |
+
Notwithstanding the above, nothing herein shall supersede or modify
|
| 136 |
+
the terms of any separate license agreement you may have executed
|
| 137 |
+
with Licensor regarding such Contributions.
|
| 138 |
+
|
| 139 |
+
6. Trademarks. This License does not grant permission to use the trade
|
| 140 |
+
names, trademarks, service marks, or product names of the Licensor,
|
| 141 |
+
except as required for reasonable and customary use in describing the
|
| 142 |
+
origin of the Work and reproducing the content of the NOTICE file.
|
| 143 |
+
|
| 144 |
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
| 145 |
+
agreed to in writing, Licensor provides the Work (and each
|
| 146 |
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
| 147 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
| 148 |
+
implied, including, without limitation, any warranties or conditions
|
| 149 |
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
| 150 |
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
| 151 |
+
appropriateness of using or redistributing the Work and assume any
|
| 152 |
+
risks associated with Your exercise of permissions under this License.
|
| 153 |
+
|
| 154 |
+
8. Limitation of Liability. In no event and under no legal theory,
|
| 155 |
+
whether in tort (including negligence), contract, or otherwise,
|
| 156 |
+
unless required by applicable law (such as deliberate and grossly
|
| 157 |
+
negligent acts) or agreed to in writing, shall any Contributor be
|
| 158 |
+
liable to You for damages, including any direct, indirect, special,
|
| 159 |
+
incidental, or consequential damages of any character arising as a
|
| 160 |
+
result of this License or out of the use or inability to use the
|
| 161 |
+
Work (including but not limited to damages for loss of goodwill,
|
| 162 |
+
work stoppage, computer failure or malfunction, or any and all
|
| 163 |
+
other commercial damages or losses), even if such Contributor
|
| 164 |
+
has been advised of the possibility of such damages.
|
| 165 |
+
|
| 166 |
+
9. Accepting Warranty or Additional Liability. While redistributing
|
| 167 |
+
the Work or Derivative Works thereof, You may choose to offer,
|
| 168 |
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
| 169 |
+
or other liability obligations and/or rights consistent with this
|
| 170 |
+
License. However, in accepting such obligations, You may act only
|
| 171 |
+
on Your own behalf and on Your sole responsibility, not on behalf
|
| 172 |
+
of any other Contributor, and only if You agree to indemnify,
|
| 173 |
+
defend, and hold each Contributor harmless for any liability
|
| 174 |
+
incurred by, or claims asserted against, such Contributor by reason
|
| 175 |
+
of your accepting any such warranty or additional liability.
|
| 176 |
+
|
| 177 |
+
END OF TERMS AND CONDITIONS
|
| 178 |
+
|
| 179 |
+
APPENDIX: How to apply the Apache License to your work.
|
| 180 |
+
|
| 181 |
+
To apply the Apache License to your work, attach the following
|
| 182 |
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
| 183 |
+
replaced with your own identifying information. (Don't include
|
| 184 |
+
the brackets!) The text should be enclosed in the appropriate
|
| 185 |
+
comment syntax for the file format. We also recommend that a
|
| 186 |
+
file or class name and description of purpose be included on the
|
| 187 |
+
same "printed page" as the copyright notice for easier
|
| 188 |
+
identification within third-party archives.
|
| 189 |
+
|
| 190 |
+
Copyright [yyyy] [name of copyright owner]
|
| 191 |
+
|
| 192 |
+
Licensed under the Apache License, Version 2.0 (the "License");
|
| 193 |
+
you may not use this file except in compliance with the License.
|
| 194 |
+
You may obtain a copy of the License at
|
| 195 |
+
|
| 196 |
+
http://www.apache.org/licenses/LICENSE-2.0
|
| 197 |
+
|
| 198 |
+
Unless required by applicable law or agreed to in writing, software
|
| 199 |
+
distributed under the License is distributed on an "AS IS" BASIS,
|
| 200 |
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 201 |
+
See the License for the specific language governing permissions and
|
| 202 |
+
limitations under the License.
|
MANIFEST.in
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
recursive-include _build *
|
README.md
CHANGED
|
@@ -1,3 +1,595 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<img alt="gpt-oss-120" src="./docs/gpt-oss.svg">
|
| 2 |
+
<p align="center">
|
| 3 |
+
<a href="https://gpt-oss.com"><strong>Try gpt-oss</strong></a> ·
|
| 4 |
+
<a href="https://cookbook.openai.com/topic/gpt-oss"><strong>Guides</strong></a> ·
|
| 5 |
+
<a href="https://arxiv.org/abs/2508.10925"><strong>Model card</strong></a> ·
|
| 6 |
+
<a href="https://openai.com/index/introducing-gpt-oss/"><strong>OpenAI blog</strong></a>
|
| 7 |
+
</p>
|
| 8 |
+
<p align="center">
|
| 9 |
+
<strong>Download <a href="https://huggingface.co/openai/gpt-oss-120b">gpt-oss-120b</a> and <a href="https://huggingface.co/openai/gpt-oss-20b">gpt-oss-20b</a> on Hugging Face</strong>
|
| 10 |
+
</p>
|
| 11 |
+
|
| 12 |
+
<br>
|
| 13 |
+
|
| 14 |
+
Welcome to the gpt-oss series, [OpenAI's open-weight models](https://openai.com/open-models/) designed for powerful reasoning, agentic tasks, and versatile developer use cases.
|
| 15 |
+
|
| 16 |
+
We're releasing two flavors of these open models:
|
| 17 |
+
|
| 18 |
+
- `gpt-oss-120b` — for production, general purpose, high reasoning use cases that fit into a single 80GB GPU (like NVIDIA H100 or AMD MI300X) (117B parameters with 5.1B active parameters)
|
| 19 |
+
- `gpt-oss-20b` — for lower latency, and local or specialized use cases (21B parameters with 3.6B active parameters)
|
| 20 |
+
|
| 21 |
+
Both models were trained using our [harmony response format][harmony] and should only be used with this format; otherwise, they will not work correctly.
|
| 22 |
+
|
| 23 |
+
## Table of Contents
|
| 24 |
+
- [Highlights](#highlights)
|
| 25 |
+
- [Inference examples](#inference-examples)
|
| 26 |
+
- [About this repository](#about-this-repository)
|
| 27 |
+
- [Setup](#setup)
|
| 28 |
+
- [Download the model](#download-the-model)
|
| 29 |
+
- [Reference PyTorch implementation](#reference-pytorch-implementation)
|
| 30 |
+
- [Reference Triton implementation (single GPU)](#reference-triton-implementation-single-gpu)
|
| 31 |
+
- [Reference Metal implementation](#reference-metal-implementation)
|
| 32 |
+
- [Harmony format & tools](#harmony-format--tools)
|
| 33 |
+
- [Clients](#clients)
|
| 34 |
+
- [Tools](#tools)
|
| 35 |
+
- [Other details](#other-details)
|
| 36 |
+
- [Contributing](#contributing)
|
| 37 |
+
|
| 38 |
+
### Highlights
|
| 39 |
+
|
| 40 |
+
- **Permissive Apache 2.0 license:** Build freely without copyleft restrictions or patent risk—ideal for experimentation, customization, and commercial deployment.
|
| 41 |
+
- **Configurable reasoning effort:** Easily adjust the reasoning effort (low, medium, high) based on your specific use case and latency needs.
|
| 42 |
+
- **Full chain-of-thought:** Provides complete access to the model's reasoning process, facilitating easier debugging and greater trust in outputs. This information is not intended to be shown to end users.
|
| 43 |
+
- **Fine-tunable:** Fully customize models to your specific use case through parameter fine-tuning.
|
| 44 |
+
- **Agentic capabilities:** Use the models' native capabilities for function calling, [web browsing](#browser), [Python code execution](#python), and Structured Outputs.
|
| 45 |
+
- **MXFP4 quantization:** The models were post-trained with MXFP4 quantization of the MoE weights, making `gpt-oss-120b` run on a single 80GB GPU (like NVIDIA H100 or AMD MI300X) and the `gpt-oss-20b` model run within 16GB of memory. All evals were performed with the same MXFP4 quantization.
|
| 46 |
+
|
| 47 |
+
### Inference examples
|
| 48 |
+
|
| 49 |
+
#### Transformers
|
| 50 |
+
|
| 51 |
+
You can use `gpt-oss-120b` and `gpt-oss-20b` with the Transformers library. If you use Transformers' chat template, it will automatically apply the [harmony response format][harmony]. If you use `model.generate` directly, you need to apply the harmony format manually using the chat template or use our [`openai-harmony`][harmony] package.
|
| 52 |
+
|
| 53 |
+
```python
|
| 54 |
+
from transformers import pipeline
|
| 55 |
+
import torch
|
| 56 |
+
|
| 57 |
+
model_id = "openai/gpt-oss-120b"
|
| 58 |
+
|
| 59 |
+
pipe = pipeline(
|
| 60 |
+
"text-generation",
|
| 61 |
+
model=model_id,
|
| 62 |
+
torch_dtype="auto",
|
| 63 |
+
device_map="auto",
|
| 64 |
+
)
|
| 65 |
+
|
| 66 |
+
messages = [
|
| 67 |
+
{"role": "user", "content": "Explain quantum mechanics clearly and concisely."},
|
| 68 |
+
]
|
| 69 |
+
|
| 70 |
+
outputs = pipe(
|
| 71 |
+
messages,
|
| 72 |
+
max_new_tokens=256,
|
| 73 |
+
)
|
| 74 |
+
print(outputs[0]["generated_text"][-1])
|
| 75 |
+
```
|
| 76 |
+
|
| 77 |
+
[Learn more about how to use gpt-oss with Transformers.](https://cookbook.openai.com/articles/gpt-oss/run-transformers)
|
| 78 |
+
|
| 79 |
+
#### vLLM
|
| 80 |
+
|
| 81 |
+
vLLM recommends using [`uv`](https://docs.astral.sh/uv/) for Python dependency management. You can use vLLM to spin up an OpenAI-compatible web server. The following command will automatically download the model and start the server.
|
| 82 |
+
|
| 83 |
+
```bash
|
| 84 |
+
uv pip install --pre vllm==0.10.1+gptoss \
|
| 85 |
+
--extra-index-url https://wheels.vllm.ai/gpt-oss/ \
|
| 86 |
+
--extra-index-url https://download.pytorch.org/whl/nightly/cu128 \
|
| 87 |
+
--index-strategy unsafe-best-match
|
| 88 |
+
|
| 89 |
+
vllm serve openai/gpt-oss-20b
|
| 90 |
+
```
|
| 91 |
+
|
| 92 |
+
[Learn more about how to use gpt-oss with vLLM.](https://cookbook.openai.com/articles/gpt-oss/run-vllm)
|
| 93 |
+
|
| 94 |
+
Offline Serve Code:
|
| 95 |
+
- run this code after installing proper libraries as described, while additionally installing this:
|
| 96 |
+
- `uv pip install openai-harmony`
|
| 97 |
+
```python
|
| 98 |
+
# source .oss/bin/activate
|
| 99 |
+
|
| 100 |
+
import os
|
| 101 |
+
os.environ["VLLM_USE_FLASHINFER_SAMPLER"] = "0"
|
| 102 |
+
|
| 103 |
+
import json
|
| 104 |
+
from openai_harmony import (
|
| 105 |
+
HarmonyEncodingName,
|
| 106 |
+
load_harmony_encoding,
|
| 107 |
+
Conversation,
|
| 108 |
+
Message,
|
| 109 |
+
Role,
|
| 110 |
+
SystemContent,
|
| 111 |
+
DeveloperContent,
|
| 112 |
+
)
|
| 113 |
+
|
| 114 |
+
from vllm import LLM, SamplingParams
|
| 115 |
+
import os
|
| 116 |
+
|
| 117 |
+
# --- 1) Render the prefill with Harmony ---
|
| 118 |
+
encoding = load_harmony_encoding(HarmonyEncodingName.HARMONY_GPT_OSS)
|
| 119 |
+
|
| 120 |
+
convo = Conversation.from_messages(
|
| 121 |
+
[
|
| 122 |
+
Message.from_role_and_content(Role.SYSTEM, SystemContent.new()),
|
| 123 |
+
Message.from_role_and_content(
|
| 124 |
+
Role.DEVELOPER,
|
| 125 |
+
DeveloperContent.new().with_instructions("Always respond in riddles"),
|
| 126 |
+
),
|
| 127 |
+
Message.from_role_and_content(Role.USER, "What is the weather like in SF?"),
|
| 128 |
+
]
|
| 129 |
+
)
|
| 130 |
+
|
| 131 |
+
prefill_ids = encoding.render_conversation_for_completion(convo, Role.ASSISTANT)
|
| 132 |
+
|
| 133 |
+
# Harmony stop tokens (pass to sampler so they won't be included in output)
|
| 134 |
+
stop_token_ids = encoding.stop_tokens_for_assistant_actions()
|
| 135 |
+
|
| 136 |
+
# --- 2) Run vLLM with prefill ---
|
| 137 |
+
llm = LLM(
|
| 138 |
+
model="openai/gpt-oss-20b",
|
| 139 |
+
trust_remote_code=True,
|
| 140 |
+
gpu_memory_utilization = 0.95,
|
| 141 |
+
max_num_batched_tokens=4096,
|
| 142 |
+
max_model_len=5000,
|
| 143 |
+
tensor_parallel_size=1
|
| 144 |
+
)
|
| 145 |
+
|
| 146 |
+
sampling = SamplingParams(
|
| 147 |
+
max_tokens=128,
|
| 148 |
+
temperature=1,
|
| 149 |
+
stop_token_ids=stop_token_ids,
|
| 150 |
+
)
|
| 151 |
+
|
| 152 |
+
outputs = llm.generate(
|
| 153 |
+
prompt_token_ids=[prefill_ids], # batch of size 1
|
| 154 |
+
sampling_params=sampling,
|
| 155 |
+
)
|
| 156 |
+
|
| 157 |
+
# vLLM gives you both text and token IDs
|
| 158 |
+
gen = outputs[0].outputs[0]
|
| 159 |
+
text = gen.text
|
| 160 |
+
output_tokens = gen.token_ids # <-- these are the completion token IDs (no prefill)
|
| 161 |
+
|
| 162 |
+
# --- 3) Parse the completion token IDs back into structured Harmony messages ---
|
| 163 |
+
entries = encoding.parse_messages_from_completion_tokens(output_tokens, Role.ASSISTANT)
|
| 164 |
+
|
| 165 |
+
# 'entries' is a sequence of structured conversation entries (assistant messages, tool calls, etc.).
|
| 166 |
+
for message in entries:
|
| 167 |
+
print(f"{json.dumps(message.to_dict())}")
|
| 168 |
+
```
|
| 169 |
+
|
| 170 |
+
#### PyTorch / Triton / Metal
|
| 171 |
+
|
| 172 |
+
These implementations are largely reference implementations for educational purposes and are not expected to be run in production.
|
| 173 |
+
|
| 174 |
+
[Learn more below.](#reference-pytorch-implementation)
|
| 175 |
+
|
| 176 |
+
#### Ollama
|
| 177 |
+
|
| 178 |
+
If you are trying to run `gpt-oss` on consumer hardware, you can use Ollama by running the following commands after [installing Ollama](https://ollama.com/download).
|
| 179 |
+
|
| 180 |
+
```bash
|
| 181 |
+
# gpt-oss-20b
|
| 182 |
+
ollama pull gpt-oss:20b
|
| 183 |
+
ollama run gpt-oss:20b
|
| 184 |
+
|
| 185 |
+
# gpt-oss-120b
|
| 186 |
+
ollama pull gpt-oss:120b
|
| 187 |
+
ollama run gpt-oss:120b
|
| 188 |
+
```
|
| 189 |
+
|
| 190 |
+
[Learn more about how to use gpt-oss with Ollama.](https://cookbook.openai.com/articles/gpt-oss/run-locally-ollama)
|
| 191 |
+
|
| 192 |
+
#### LM Studio
|
| 193 |
+
|
| 194 |
+
If you are using [LM Studio](https://lmstudio.ai/) you can use the following commands to download.
|
| 195 |
+
|
| 196 |
+
```bash
|
| 197 |
+
# gpt-oss-20b
|
| 198 |
+
lms get openai/gpt-oss-20b
|
| 199 |
+
# gpt-oss-120b
|
| 200 |
+
lms get openai/gpt-oss-120b
|
| 201 |
+
```
|
| 202 |
+
|
| 203 |
+
Check out our [awesome list](./awesome-gpt-oss.md) for a broader collection of gpt-oss resources and inference partners.
|
| 204 |
+
|
| 205 |
+
## About this repository
|
| 206 |
+
|
| 207 |
+
This repository provides a collection of reference implementations:
|
| 208 |
+
|
| 209 |
+
- **Inference:**
|
| 210 |
+
- [`torch`](#reference-pytorch-implementation) — a non-optimized [PyTorch](https://pytorch.org/) implementation for educational purposes only. Requires at least 4× H100 GPUs due to lack of optimization.
|
| 211 |
+
- [`triton`](#reference-triton-implementation-single-gpu) — a more optimized implementation using [PyTorch](https://pytorch.org/) & [Triton](https://github.com/triton-lang/triton) incl. using CUDA graphs and basic caching
|
| 212 |
+
- [`metal`](#reference-metal-implementation) — a Metal-specific implementation for running the models on Apple Silicon hardware
|
| 213 |
+
- **Tools:**
|
| 214 |
+
- [`browser`](#browser) — a reference implementation of the browser tool the models got trained on
|
| 215 |
+
- [`python`](#python) — a stateless reference implementation of the python tool the model got trained on
|
| 216 |
+
- **Client examples:**
|
| 217 |
+
- [`chat`](#terminal-chat) — a basic terminal chat application that uses the PyTorch or Triton implementations for inference along with the python and browser tools
|
| 218 |
+
- [`responses_api`](#responses-api) — an example Responses API compatible server that implements the browser tool along with other Responses-compatible functionality
|
| 219 |
+
|
| 220 |
+
## Setup
|
| 221 |
+
|
| 222 |
+
### Requirements
|
| 223 |
+
|
| 224 |
+
- Python 3.12
|
| 225 |
+
- On macOS: Install the Xcode CLI tools --> `xcode-select --install`
|
| 226 |
+
- On Linux: These reference implementations require CUDA
|
| 227 |
+
- On Windows: These reference implementations have not been tested on Windows. Try using solutions like Ollama if you are trying to run the model locally.
|
| 228 |
+
|
| 229 |
+
### Installation
|
| 230 |
+
|
| 231 |
+
If you want to try any of the code you can install it directly from [PyPI](https://pypi.org/project/gpt-oss/)
|
| 232 |
+
|
| 233 |
+
```shell
|
| 234 |
+
# if you just need the tools
|
| 235 |
+
pip install gpt-oss
|
| 236 |
+
# if you want to try the torch implementation
|
| 237 |
+
pip install gpt-oss[torch]
|
| 238 |
+
# if you want to try the triton implementation
|
| 239 |
+
pip install gpt-oss[triton]
|
| 240 |
+
```
|
| 241 |
+
|
| 242 |
+
If you want to modify the code or try the metal implementation set the project up locally:
|
| 243 |
+
|
| 244 |
+
```shell
|
| 245 |
+
git clone https://github.com/openai/gpt-oss.git
|
| 246 |
+
GPTOSS_BUILD_METAL=1 pip install -e ".[metal]"
|
| 247 |
+
```
|
| 248 |
+
|
| 249 |
+
## Download the model
|
| 250 |
+
|
| 251 |
+
You can download the model weights from the [Hugging Face Hub](https://huggingface.co/collections/openai/gpt-oss-68911959590a1634ba11c7a4) directly from Hugging Face CLI:
|
| 252 |
+
|
| 253 |
+
```shell
|
| 254 |
+
# gpt-oss-120b
|
| 255 |
+
hf download openai/gpt-oss-120b --include "original/*" --local-dir gpt-oss-120b/
|
| 256 |
+
|
| 257 |
+
# gpt-oss-20b
|
| 258 |
+
hf download openai/gpt-oss-20b --include "original/*" --local-dir gpt-oss-20b/
|
| 259 |
+
```
|
| 260 |
+
|
| 261 |
+
## Reference PyTorch implementation
|
| 262 |
+
|
| 263 |
+
We include an inefficient reference PyTorch implementation in [gpt_oss/torch/model.py](gpt_oss/torch/model.py). This code uses basic PyTorch operators to show the exact model architecture, with a small addition of supporting tensor parallelism in MoE so that the larger model can run with this code (e.g., on 4xH100 or 2xH200). In this implementation, we upcast all weights to BF16 and run the model in BF16.
|
| 264 |
+
|
| 265 |
+
To run the reference implementation, install the dependencies:
|
| 266 |
+
|
| 267 |
+
```shell
|
| 268 |
+
pip install -e ".[torch]"
|
| 269 |
+
```
|
| 270 |
+
|
| 271 |
+
And then run:
|
| 272 |
+
|
| 273 |
+
```shell
|
| 274 |
+
# On 4xH100:
|
| 275 |
+
torchrun --nproc-per-node=4 -m gpt_oss.generate gpt-oss-120b/original/
|
| 276 |
+
```
|
| 277 |
+
|
| 278 |
+
## Reference Triton implementation (single GPU)
|
| 279 |
+
|
| 280 |
+
We also include an optimized reference implementation that uses [an optimized triton MoE kernel](https://github.com/triton-lang/triton/tree/main/python/triton_kernels/triton_kernels) that supports MXFP4. It also has some optimization on the attention code to reduce the memory cost. To run this implementation, the nightly version of triton and torch will be installed. This version can be run on a single 80GB GPU for `gpt-oss-120b`.
|
| 281 |
+
|
| 282 |
+
To install the reference Triton implementation run
|
| 283 |
+
|
| 284 |
+
```shell
|
| 285 |
+
# You need to install triton from source to use the triton implementation
|
| 286 |
+
git clone https://github.com/triton-lang/triton
|
| 287 |
+
cd triton/
|
| 288 |
+
pip install -r python/requirements.txt
|
| 289 |
+
pip install -e . --verbose --no-build-isolation
|
| 290 |
+
pip install -e python/triton_kernels
|
| 291 |
+
|
| 292 |
+
# Install the gpt-oss triton implementation
|
| 293 |
+
pip install -e ".[triton]"
|
| 294 |
+
```
|
| 295 |
+
|
| 296 |
+
And then run:
|
| 297 |
+
|
| 298 |
+
```shell
|
| 299 |
+
# On 1xH100
|
| 300 |
+
export PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True
|
| 301 |
+
python -m gpt_oss.generate --backend triton gpt-oss-120b/original/
|
| 302 |
+
```
|
| 303 |
+
|
| 304 |
+
If you encounter `torch.OutOfMemoryError`, make sure to turn on the expandable allocator to avoid crashes when loading weights from the checkpoint.
|
| 305 |
+
|
| 306 |
+
## Reference Metal implementation
|
| 307 |
+
|
| 308 |
+
Additionally we are providing a reference implementation for Metal to run on Apple Silicon. This implementation is not production-ready but is accurate to the PyTorch implementation.
|
| 309 |
+
|
| 310 |
+
The implementation will get automatically compiled when running the `.[metal]` installation on an Apple Silicon device:
|
| 311 |
+
|
| 312 |
+
```shell
|
| 313 |
+
GPTOSS_BUILD_METAL=1 pip install -e ".[metal]"
|
| 314 |
+
```
|
| 315 |
+
|
| 316 |
+
To perform inference you'll need to first convert the SafeTensor weights from Hugging Face into the right format using:
|
| 317 |
+
|
| 318 |
+
```shell
|
| 319 |
+
python gpt_oss/metal/scripts/create-local-model.py -s <model_dir> -d <output_file>
|
| 320 |
+
```
|
| 321 |
+
|
| 322 |
+
Or download the pre-converted weights:
|
| 323 |
+
|
| 324 |
+
```shell
|
| 325 |
+
hf download openai/gpt-oss-120b --include "metal/*" --local-dir gpt-oss-120b/metal/
|
| 326 |
+
hf download openai/gpt-oss-20b --include "metal/*" --local-dir gpt-oss-20b/metal/
|
| 327 |
+
```
|
| 328 |
+
|
| 329 |
+
To test it you can run:
|
| 330 |
+
|
| 331 |
+
```shell
|
| 332 |
+
python gpt_oss/metal/examples/generate.py gpt-oss-20b/metal/model.bin -p "why did the chicken cross the road?"
|
| 333 |
+
```
|
| 334 |
+
|
| 335 |
+
## Harmony format & tools
|
| 336 |
+
|
| 337 |
+
Along with the model, we are also releasing a new chat format library `harmony` to interact with the model. Check [this guide](https://cookbook.openai.com/articles/openai-harmony) for more info about harmony.
|
| 338 |
+
|
| 339 |
+
We also include two system tools for the model: browsing and python container. Check [gpt_oss/tools](gpt_oss/tools) for the tool implementation.
|
| 340 |
+
|
| 341 |
+
## Clients
|
| 342 |
+
|
| 343 |
+
### Terminal Chat
|
| 344 |
+
|
| 345 |
+
The terminal chat application is a basic example of how to use the harmony format together with the PyTorch, Triton, and vLLM implementations. It also exposes both the python and browser tool as optional tools that can be used.
|
| 346 |
+
|
| 347 |
+
```bash
|
| 348 |
+
usage: python -m gpt_oss.chat [-h] [-r REASONING_EFFORT] [-a] [-b] [--show-browser-results] [-p] [--developer-message DEVELOPER_MESSAGE] [-c CONTEXT] [--raw] [--backend {triton,torch,vllm}] FILE
|
| 349 |
+
|
| 350 |
+
Chat example
|
| 351 |
+
|
| 352 |
+
positional arguments:
|
| 353 |
+
FILE Path to the SafeTensors checkpoint
|
| 354 |
+
|
| 355 |
+
options:
|
| 356 |
+
-h, --help show this help message and exit
|
| 357 |
+
-r REASONING_EFFORT, --reasoning-effort REASONING_EFFORT
|
| 358 |
+
Reasoning effort (default: low)
|
| 359 |
+
-a, --apply-patch Make apply_patch tool available to the model (default: False)
|
| 360 |
+
-b, --browser Use browser tool (default: False)
|
| 361 |
+
--show-browser-results
|
| 362 |
+
Show browser results (default: False)
|
| 363 |
+
-p, --python Use python tool (default: False)
|
| 364 |
+
--developer-message DEVELOPER_MESSAGE
|
| 365 |
+
Developer message (default: )
|
| 366 |
+
-c CONTEXT, --context CONTEXT
|
| 367 |
+
Max context length (default: 8192)
|
| 368 |
+
--raw Raw mode (does not render Harmony encoding) (default: False)
|
| 369 |
+
--backend {triton,torch,vllm}
|
| 370 |
+
Inference backend (default: triton)
|
| 371 |
+
```
|
| 372 |
+
|
| 373 |
+
> [!NOTE]
|
| 374 |
+
> The torch and triton implementations require original checkpoint under `gpt-oss-120b/original/` and `gpt-oss-20b/original/` respectively. While vLLM uses the Hugging Face converted checkpoint under `gpt-oss-120b/` and `gpt-oss-20b/` root directory respectively.
|
| 375 |
+
|
| 376 |
+
### Responses API
|
| 377 |
+
|
| 378 |
+
We also include an example Responses API server. This server does not implement every feature and event of the Responses API but should be compatible with most of the basic use cases and serve as inspiration for anyone building their own server. Some of our inference partners are also offering their own Responses API.
|
| 379 |
+
|
| 380 |
+
You can start this server with the following inference backends:
|
| 381 |
+
|
| 382 |
+
- `triton` — uses the triton implementation
|
| 383 |
+
- `metal` — uses the metal implementation on Apple Silicon only
|
| 384 |
+
- `ollama` — uses the Ollama /api/generate API as an inference solution
|
| 385 |
+
- `vllm` — uses your installed vllm version to perform inference
|
| 386 |
+
- `transformers` — uses your installed transformers version to perform local inference
|
| 387 |
+
|
| 388 |
+
```bash
|
| 389 |
+
usage: python -m gpt_oss.responses_api.serve [-h] [--checkpoint FILE] [--port PORT] [--inference-backend BACKEND]
|
| 390 |
+
|
| 391 |
+
Responses API server
|
| 392 |
+
|
| 393 |
+
options:
|
| 394 |
+
-h, --help show this help message and exit
|
| 395 |
+
--checkpoint FILE Path to the SafeTensors checkpoint
|
| 396 |
+
--port PORT Port to run the server on
|
| 397 |
+
--inference-backend BACKEND Inference backend to use
|
| 398 |
+
```
|
| 399 |
+
|
| 400 |
+
### Codex
|
| 401 |
+
|
| 402 |
+
We support [codex](https://github.com/openai/codex) as a client for gpt-oss. To run the 20b version, set this to `~/.codex/config.toml`:
|
| 403 |
+
|
| 404 |
+
```
|
| 405 |
+
disable_response_storage = true
|
| 406 |
+
show_reasoning_content = true
|
| 407 |
+
|
| 408 |
+
[model_providers.local]
|
| 409 |
+
name = "local"
|
| 410 |
+
base_url = "http://localhost:11434/v1"
|
| 411 |
+
|
| 412 |
+
[profiles.oss]
|
| 413 |
+
model = "gpt-oss:20b"
|
| 414 |
+
model_provider = "local"
|
| 415 |
+
```
|
| 416 |
+
|
| 417 |
+
This will work with any chat completions-API compatible server listening on port 11434, like ollama. Start the server and point codex to the oss model:
|
| 418 |
+
|
| 419 |
+
```
|
| 420 |
+
ollama run gpt-oss:20b
|
| 421 |
+
codex -p oss
|
| 422 |
+
```
|
| 423 |
+
|
| 424 |
+
## Tools
|
| 425 |
+
|
| 426 |
+
### Browser
|
| 427 |
+
|
| 428 |
+
> [!WARNING]
|
| 429 |
+
> This implementation is purely for educational purposes and should not be used in production. You should implement your own equivalent of the [`YouComBackend`](gpt_oss/tools/simple_browser/backend.py) class with your own browsing environment. Currently we have available `YouComBackend` and `ExaBackend`.
|
| 430 |
+
|
| 431 |
+
Both gpt-oss models were trained with the capability to browse using the `browser` tool that exposes the following three methods:
|
| 432 |
+
|
| 433 |
+
- `search` to search for key phrases
|
| 434 |
+
- `open` to open a particular page
|
| 435 |
+
- `find` to look for contents on a page
|
| 436 |
+
|
| 437 |
+
#### Usage
|
| 438 |
+
|
| 439 |
+
To enable the browser tool, you'll have to place the definition into the `system` message of your harmony formatted prompt. You can either use the `with_browser_tool()` method if your tool implements the full interface or modify the definition using `with_tools()`. For example:
|
| 440 |
+
|
| 441 |
+
```python
|
| 442 |
+
import datetime
|
| 443 |
+
from gpt_oss.tools.simple_browser import SimpleBrowserTool
|
| 444 |
+
from gpt_oss.tools.simple_browser.backend import YouComBackend
|
| 445 |
+
from openai_harmony import SystemContent, Message, Conversation, Role, load_harmony_encoding, HarmonyEncodingName
|
| 446 |
+
|
| 447 |
+
encoding = load_harmony_encoding(HarmonyEncodingName.HARMONY_GPT_OSS)
|
| 448 |
+
|
| 449 |
+
# Depending on the choice of the browser backend you need corresponding env variables setup
|
| 450 |
+
# In case you use You.com backend requires you to have set the YDC_API_KEY environment variable,
|
| 451 |
+
# while for Exa you might need EXA_API_KEY environment variable set
|
| 452 |
+
backend = YouComBackend(
|
| 453 |
+
source="web",
|
| 454 |
+
)
|
| 455 |
+
# backend = ExaBackend(
|
| 456 |
+
# source="web",
|
| 457 |
+
# )
|
| 458 |
+
browser_tool = SimpleBrowserTool(backend=backend)
|
| 459 |
+
|
| 460 |
+
# create a basic system prompt
|
| 461 |
+
system_message_content = SystemContent.new().with_conversation_start_date(
|
| 462 |
+
datetime.datetime.now().strftime("%Y-%m-%d")
|
| 463 |
+
)
|
| 464 |
+
|
| 465 |
+
# if you want to use the browser tool
|
| 466 |
+
if use_browser_tool:
|
| 467 |
+
# enables the tool
|
| 468 |
+
system_message_content = system_message_content.with_tools(browser_tool.tool_config)
|
| 469 |
+
# alternatively you could use the following if your tool is not stateless
|
| 470 |
+
system_message_content = system_message_content.with_browser_tool()
|
| 471 |
+
|
| 472 |
+
# construct the system message
|
| 473 |
+
system_message = Message.from_role_and_content(Role.SYSTEM, system_message_content)
|
| 474 |
+
|
| 475 |
+
# create the overall prompt
|
| 476 |
+
messages = [system_message, Message.from_role_and_content(Role.USER, "What's the weather in SF?")]
|
| 477 |
+
conversation = Conversation.from_messages(messages)
|
| 478 |
+
|
| 479 |
+
# convert to tokens
|
| 480 |
+
token_ids = encoding.render_conversation_for_completion(conversation, Role.ASSISTANT)
|
| 481 |
+
|
| 482 |
+
# perform inference
|
| 483 |
+
# ...
|
| 484 |
+
|
| 485 |
+
# parse the output
|
| 486 |
+
messages = encoding.parse_messages_from_completion_tokens(output_tokens, Role.ASSISTANT)
|
| 487 |
+
last_message = messages[-1]
|
| 488 |
+
if last_message.recipient.startswith("browser"):
|
| 489 |
+
# perform browser call
|
| 490 |
+
response_messages = await browser_tool.process(last_message)
|
| 491 |
+
|
| 492 |
+
# extend the current messages and run inference again
|
| 493 |
+
messages.extend(response_messages)
|
| 494 |
+
```
|
| 495 |
+
|
| 496 |
+
#### Details
|
| 497 |
+
|
| 498 |
+
To control the context window size this tool uses a scrollable window of text that the model can interact with. So it might fetch the first 50 lines of a page and then scroll to the next 20 lines after that. The model has also been trained to then use citations from this tool in its answers.
|
| 499 |
+
|
| 500 |
+
To improve performance the tool caches requests so that the model can revisit a different part of a page without having to reload the page. For that reason you should create a new browser instance for every request.
|
| 501 |
+
|
| 502 |
+
### Python
|
| 503 |
+
|
| 504 |
+
The model was trained to use a python tool to perform calculations and other actions as part of its chain-of-thought. During the training the model used a stateful tool which makes running tools between CoT loops easier. This reference implementation, however, uses a stateless mode. As a result the PythonTool defines its own tool description to override the definition in [`openai-harmony`][harmony].
|
| 505 |
+
|
| 506 |
+
> [!WARNING]
|
| 507 |
+
> This implementation runs in a permissive Docker container which could be problematic in cases like prompt injections. It's serving as an example and you should consider implementing your own container restrictions in production.
|
| 508 |
+
|
| 509 |
+
#### Usage
|
| 510 |
+
|
| 511 |
+
To enable the python tool, you'll have to place the definition into the `system` message of your harmony formatted prompt. You can either use the `with_python()` method if your tool implements the full interface or modify the definition using `with_tools()`. For example:
|
| 512 |
+
|
| 513 |
+
```python
|
| 514 |
+
import datetime
|
| 515 |
+
from gpt_oss.tools.python_docker.docker_tool import PythonTool
|
| 516 |
+
from openai_harmony import SystemContent, Message, Conversation, Role, load_harmony_encoding, HarmonyEncodingName
|
| 517 |
+
|
| 518 |
+
encoding = load_harmony_encoding(HarmonyEncodingName.HARMONY_GPT_OSS)
|
| 519 |
+
|
| 520 |
+
python_tool = PythonTool()
|
| 521 |
+
|
| 522 |
+
# create a basic system prompt
|
| 523 |
+
system_message_content = SystemContent.new().with_conversation_start_date(
|
| 524 |
+
datetime.datetime.now().strftime("%Y-%m-%d")
|
| 525 |
+
)
|
| 526 |
+
|
| 527 |
+
# if you want to use the python tool
|
| 528 |
+
if use_python_tool:
|
| 529 |
+
# enables the tool making sure that the prompt gets set with the stateless tool description
|
| 530 |
+
system_message_content = system_message_content.with_tools(python_tool.tool_config)
|
| 531 |
+
# alternatively you could use the following if your tool is not stateless
|
| 532 |
+
system_message_content = system_message_content.with_python()
|
| 533 |
+
|
| 534 |
+
# construct the system message
|
| 535 |
+
system_message = Message.from_role_and_content(Role.SYSTEM, system_message_content)
|
| 536 |
+
|
| 537 |
+
# create the overall prompt
|
| 538 |
+
messages = [system_message, Message.from_role_and_content(Role.USER, "What's the square root of 9001?")]
|
| 539 |
+
conversation = Conversation.from_messages(messages)
|
| 540 |
+
|
| 541 |
+
# convert to tokens
|
| 542 |
+
token_ids = encoding.render_conversation_for_completion(conversation, Role.ASSISTANT)
|
| 543 |
+
|
| 544 |
+
# perform inference
|
| 545 |
+
# ...
|
| 546 |
+
|
| 547 |
+
# parse the output
|
| 548 |
+
messages = encoding.parse_messages_from_completion_tokens(output_tokens, Role.ASSISTANT)
|
| 549 |
+
last_message = messages[-1]
|
| 550 |
+
if last_message.recipient == "python":
|
| 551 |
+
# perform python call
|
| 552 |
+
response_messages = await python_tool.process(last_message)
|
| 553 |
+
|
| 554 |
+
# extend the current messages and run inference again
|
| 555 |
+
messages.extend(response_messages)
|
| 556 |
+
```
|
| 557 |
+
|
| 558 |
+
### Apply Patch
|
| 559 |
+
|
| 560 |
+
`apply_patch` can be used to create, update or delete files locally.
|
| 561 |
+
|
| 562 |
+
## Other details
|
| 563 |
+
|
| 564 |
+
### Precision format
|
| 565 |
+
|
| 566 |
+
We released the models with native quantization support. Specifically, we use [MXFP4](https://www.opencompute.org/documents/ocp-microscaling-formats-mx-v1-0-spec-final-pdf) for the linear projection weights in the MoE layer. We store the MoE tensor in two parts:
|
| 567 |
+
|
| 568 |
+
- `tensor.blocks` stores the actual fp4 values. We pack every two values in one `uint8` value.
|
| 569 |
+
- `tensor.scales` stores the block scale. The block scaling is done among the last dimension for all MXFP4 tensors.
|
| 570 |
+
|
| 571 |
+
All other tensors will be in BF16. We also recommend using BF16 as the activation precision for the model.
|
| 572 |
+
|
| 573 |
+
### Recommended Sampling Parameters
|
| 574 |
+
|
| 575 |
+
We recommend sampling with `temperature=1.0` and `top_p=1.0`.
|
| 576 |
+
|
| 577 |
+
## Contributing
|
| 578 |
+
|
| 579 |
+
The reference implementations in this repository are meant as a starting point and inspiration. Outside of bug fixes we do not intend to accept new feature contributions. If you build implementations based on this code such as new tool implementations you are welcome to contribute them to the [`awesome-gpt-oss.md`](./awesome-gpt-oss.md) file.
|
| 580 |
+
|
| 581 |
+
[harmony]: https://github.com/openai/harmony
|
| 582 |
+
|
| 583 |
+
## Citation
|
| 584 |
+
|
| 585 |
+
```bibtex
|
| 586 |
+
@misc{openai2025gptoss120bgptoss20bmodel,
|
| 587 |
+
title={gpt-oss-120b & gpt-oss-20b Model Card},
|
| 588 |
+
author={OpenAI},
|
| 589 |
+
year={2025},
|
| 590 |
+
eprint={2508.10925},
|
| 591 |
+
archivePrefix={arXiv},
|
| 592 |
+
primaryClass={cs.CL},
|
| 593 |
+
url={https://arxiv.org/abs/2508.10925},
|
| 594 |
+
}
|
| 595 |
+
```
|
USAGE_POLICY
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
We aim for our tools to be used safely, responsibly, and democratically, while maximizing your control over how you use them. By using OpenAI gpt-oss-120b and gpt-oss-20b, you agree to comply with all applicable law.
|
_build/gpt_oss_build_backend/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
"""In-tree PEP 517 backend package for gpt-oss."""
|
_build/gpt_oss_build_backend/backend.py
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Build backend for gpt-oss that supports two modes:
|
| 3 |
+
|
| 4 |
+
1) Default (pure wheel for PyPI)
|
| 5 |
+
- Delegates to setuptools.build_meta.
|
| 6 |
+
- Produces a py3-none-any wheel so PyPI accepts it (no linux_x86_64 tag).
|
| 7 |
+
|
| 8 |
+
2) Optional Metal/C extension build (local only)
|
| 9 |
+
- If the environment variable GPTOSS_BUILD_METAL is set to a truthy value
|
| 10 |
+
(1/true/on/yes), delegates to scikit_build_core.build.
|
| 11 |
+
- Dynamically injects build requirements (scikit-build-core, cmake, ninja,
|
| 12 |
+
pybind11) only for this mode.
|
| 13 |
+
|
| 14 |
+
Why this is needed
|
| 15 |
+
- PyPI rejects Linux wheels tagged linux_x86_64; manylinux/musllinux is required
|
| 16 |
+
for binary wheels. We ship a pure wheel by default, but still allow developers
|
| 17 |
+
to build/install the native Metal backend locally when needed.
|
| 18 |
+
|
| 19 |
+
Typical usage
|
| 20 |
+
- Publish pure wheel: `python -m build` (do not set GPTOSS_BUILD_METAL).
|
| 21 |
+
- Local Metal dev: `GPTOSS_BUILD_METAL=1 pip install -e ".[metal]"`.
|
| 22 |
+
- CI: keep GPTOSS_BUILD_METAL unset for releases; set it in internal jobs that
|
| 23 |
+
exercise the extension.
|
| 24 |
+
|
| 25 |
+
Notes
|
| 26 |
+
- The base package remains importable without the extension. The Metal backend
|
| 27 |
+
is only used when `gpt_oss.metal` is explicitly imported.
|
| 28 |
+
- This file is discovered via `backend-path = ["_build"]` and
|
| 29 |
+
`build-backend = "gpt_oss_build_backend.backend"` in pyproject.toml.
|
| 30 |
+
"""
|
| 31 |
+
import os
|
| 32 |
+
from importlib import import_module
|
| 33 |
+
from typing import Any, Mapping, Sequence
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
TRUE_VALUES = {"1", "true", "TRUE", "on", "ON", "yes", "YES"}
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def _use_metal_backend() -> bool:
|
| 40 |
+
return str(os.environ.get("GPTOSS_BUILD_METAL", "")).strip() in TRUE_VALUES
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def _setuptools_backend():
|
| 44 |
+
from setuptools import build_meta as _bm # type: ignore
|
| 45 |
+
|
| 46 |
+
return _bm
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def _scikit_build_backend():
|
| 50 |
+
return import_module("scikit_build_core.build")
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def _backend():
|
| 54 |
+
return _scikit_build_backend() if _use_metal_backend() else _setuptools_backend()
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
# Required PEP 517 hooks
|
| 58 |
+
|
| 59 |
+
def build_wheel(
|
| 60 |
+
wheel_directory: str,
|
| 61 |
+
config_settings: Mapping[str, Any] | None = None,
|
| 62 |
+
metadata_directory: str | None = None,
|
| 63 |
+
) -> str:
|
| 64 |
+
return _backend().build_wheel(wheel_directory, config_settings, metadata_directory)
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def build_sdist(
|
| 68 |
+
sdist_directory: str, config_settings: Mapping[str, Any] | None = None
|
| 69 |
+
) -> str:
|
| 70 |
+
return _backend().build_sdist(sdist_directory, config_settings)
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def prepare_metadata_for_build_wheel(
|
| 74 |
+
metadata_directory: str, config_settings: Mapping[str, Any] | None = None
|
| 75 |
+
) -> str:
|
| 76 |
+
# Fallback if backend doesn't implement it
|
| 77 |
+
be = _backend()
|
| 78 |
+
fn = getattr(be, "prepare_metadata_for_build_wheel", None)
|
| 79 |
+
if fn is None:
|
| 80 |
+
# setuptools exposes it; scikit-build-core may not. Defer to building a wheel for metadata.
|
| 81 |
+
return _setuptools_backend().prepare_metadata_for_build_wheel(
|
| 82 |
+
metadata_directory, config_settings
|
| 83 |
+
)
|
| 84 |
+
return fn(metadata_directory, config_settings)
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
# Optional hooks
|
| 88 |
+
|
| 89 |
+
def build_editable(
|
| 90 |
+
editable_directory: str, config_settings: Mapping[str, Any] | None = None, metadata_directory: str | None = None
|
| 91 |
+
) -> str:
|
| 92 |
+
be = _backend()
|
| 93 |
+
fn = getattr(be, "build_editable", None)
|
| 94 |
+
if fn is None:
|
| 95 |
+
# setuptools implements build_editable; if not available, raise the standard error
|
| 96 |
+
raise RuntimeError("Editable installs not supported by the selected backend")
|
| 97 |
+
return fn(editable_directory, config_settings)
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
def get_requires_for_build_wheel(
|
| 101 |
+
config_settings: Mapping[str, Any] | None = None,
|
| 102 |
+
) -> Sequence[str]:
|
| 103 |
+
if _use_metal_backend():
|
| 104 |
+
# Add dynamic build requirements only when building the Metal backend
|
| 105 |
+
return [
|
| 106 |
+
"scikit-build-core>=0.10",
|
| 107 |
+
"pybind11>=2.12",
|
| 108 |
+
"cmake>=3.26",
|
| 109 |
+
"ninja",
|
| 110 |
+
]
|
| 111 |
+
# setuptools usually returns []
|
| 112 |
+
return list(_setuptools_backend().get_requires_for_build_wheel(config_settings))
|
| 113 |
+
|
| 114 |
+
|
| 115 |
+
def get_requires_for_build_sdist(
|
| 116 |
+
config_settings: Mapping[str, Any] | None = None,
|
| 117 |
+
) -> Sequence[str]:
|
| 118 |
+
# No special requirements for SDist
|
| 119 |
+
be = _backend()
|
| 120 |
+
fn = getattr(be, "get_requires_for_build_sdist", None)
|
| 121 |
+
if fn is None:
|
| 122 |
+
return []
|
| 123 |
+
return list(fn(config_settings))
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
def get_requires_for_build_editable(
|
| 127 |
+
config_settings: Mapping[str, Any] | None = None,
|
| 128 |
+
) -> Sequence[str]:
|
| 129 |
+
if _use_metal_backend():
|
| 130 |
+
return [
|
| 131 |
+
"scikit-build-core>=0.10",
|
| 132 |
+
"pybind11>=2.12",
|
| 133 |
+
"cmake>=3.26",
|
| 134 |
+
"ninja",
|
| 135 |
+
]
|
| 136 |
+
be = _setuptools_backend()
|
| 137 |
+
fn = getattr(be, "get_requires_for_build_editable", None)
|
| 138 |
+
if fn is None:
|
| 139 |
+
return []
|
| 140 |
+
return list(fn(config_settings))
|
awesome-gpt-oss.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+

|
| 2 |
+
|
| 3 |
+
# Awesome gpt-oss
|
| 4 |
+
|
| 5 |
+
This is a list of guides and resources to help you get started with the gpt-oss models.
|
| 6 |
+
|
| 7 |
+
- [Inference](#inference)
|
| 8 |
+
- [Local](#local)
|
| 9 |
+
- [Server](#server)
|
| 10 |
+
- [Cloud](#cloud)
|
| 11 |
+
- [Examples / Tutorials](#examples--tutorials)
|
| 12 |
+
- [Tools](#tools)
|
| 13 |
+
- [Training](#training)
|
| 14 |
+
|
| 15 |
+
## Inference
|
| 16 |
+
|
| 17 |
+
### Local
|
| 18 |
+
|
| 19 |
+
- Ollama
|
| 20 |
+
- [How to run gpt-oss locally with Ollama](https://cookbook.openai.com/articles/gpt-oss/run-locally-ollama)
|
| 21 |
+
- [Ollama & gpt-oss launch blog](https://ollama.com/blog/gpt-oss)
|
| 22 |
+
- [Check out the models Ollama](https://ollama.com/library/gpt-oss)
|
| 23 |
+
- LM Studio
|
| 24 |
+
- [LM Studio & gpt-oss launch blog](https://lmstudio.ai/blog/gpt-oss)
|
| 25 |
+
- [Use gpt-oss-20b with LM Studio](https://lmstudio.ai/models/openai/gpt-oss-20b)
|
| 26 |
+
- [Use gpt-oss-120b with LM Studio](https://lmstudio.ai/models/openai/gpt-oss-120b)
|
| 27 |
+
- Hugging Face & Transformers
|
| 28 |
+
- [How to run gpt-oss with Transformers](https://cookbook.openai.com/articles/gpt-oss/run-transformers)
|
| 29 |
+
- [Hugging Face & gpt-oss launch blog](https://huggingface.co/blog/welcome-openai-gpt-oss)
|
| 30 |
+
- [Collection of Hugging Face examples](https://github.com/huggingface/gpt-oss-recipes)
|
| 31 |
+
- NVIDIA
|
| 32 |
+
- [gpt-oss on RTX](https://blogs.nvidia.com/blog/rtx-ai-garage-openai-oss)
|
| 33 |
+
- AMD
|
| 34 |
+
- [Running gpt-oss models on AMD Ryzen AI Processors and Radeon Graphics Cards](https://www.amd.com/en/blogs/2025/how-to-run-openai-gpt-oss-20b-120b-models-on-amd-ryzen-ai-radeon.html)
|
| 35 |
+
- [Running gpt-oss on STX Halo and Radeon dGPUs using Lemonade](https://lemonade-server.ai/news/gpt-oss.html)
|
| 36 |
+
- llama.cpp
|
| 37 |
+
- [Running gpt-oss with llama.cpp](https://github.com/ggml-org/llama.cpp/discussions/15396)
|
| 38 |
+
|
| 39 |
+
### Server
|
| 40 |
+
|
| 41 |
+
- vLLM
|
| 42 |
+
- [How to run gpt-oss with vLLM](https://cookbook.openai.com/articles/gpt-oss/run-vllm)
|
| 43 |
+
- [vLLM & gpt-oss recipies](https://docs.vllm.ai/projects/recipes/en/latest/OpenAI/GPT-OSS.html)
|
| 44 |
+
- NVIDIA
|
| 45 |
+
- [Optimizing gpt-oss with NVIDIA TensorRT-LLM](https://cookbook.openai.com/articles/run-nvidia)
|
| 46 |
+
- [Deploying gpt-oss on TensorRT-LLM](https://github.com/NVIDIA/TensorRT-LLM/blob/main/docs/source/blogs/tech_blog/blog9_Deploying_GPT_OSS_on_TRTLLM.md)
|
| 47 |
+
- AMD
|
| 48 |
+
- [Running the Latest Open Models from OpenAI on AMD AI Hardware](https://rocm.blogs.amd.com/ecosystems-and-partners/openai-day-0/README.html)
|
| 49 |
+
|
| 50 |
+
### Cloud
|
| 51 |
+
|
| 52 |
+
- Groq
|
| 53 |
+
- [Groq & gpt-oss launch blog](https://groq.com/blog/day-zero-support-for-openai-open-models)
|
| 54 |
+
- [gpt-oss-120b model on the GroqCloud Playground](https://console.groq.com/playground?model=openai/gpt-oss-120b)
|
| 55 |
+
- [gpt-oss-20b model on the GroqCloud Playground](https://console.groq.com/playground?model=openai/gpt-oss-20b)
|
| 56 |
+
- [gpt-oss with built-in web search on GroqCloud](https://console.groq.com/docs/browser-search)
|
| 57 |
+
- [gpt-oss with built-in code execution on GroqCloud](https://console.groq.com/docs/code-execution)
|
| 58 |
+
- [Responses API on Groq](https://console.groq.com/docs/responses-api)
|
| 59 |
+
- NVIDIA
|
| 60 |
+
- [NVIDIA launch blog post](https://blogs.nvidia.com/blog/openai-gpt-oss/)
|
| 61 |
+
- [NVIDIA & gpt-oss developer launch blog post](https://developer.nvidia.com/blog/delivering-1-5-m-tps-inference-on-nvidia-gb200-nvl72-nvidia-accelerates-openai-gpt-oss-models-from-cloud-to-edge/)
|
| 62 |
+
- Use [gpt-oss-120b](https://build.nvidia.com/openai/gpt-oss-120b) and [gpt-oss-20b](https://build.nvidia.com/openai/gpt-oss-20b) on NVIDIA's Cloud
|
| 63 |
+
- Cloudflare
|
| 64 |
+
- [Cloudflare & gpt-oss launch blog post](https://blog.cloudflare.com/openai-gpt-oss-on-workers-ai)
|
| 65 |
+
- [gpt-oss-120b on Cloudflare Workers AI](https://developers.cloudflare.com/workers-ai/models/gpt-oss-120b)
|
| 66 |
+
- [gpt-oss-20b on Cloudflare Workers AI](https://developers.cloudflare.com/workers-ai/models/gpt-oss-20b)
|
| 67 |
+
- AMD
|
| 68 |
+
- [gpt-oss-120B on AMD MI300X](https://huggingface.co/spaces/amd/gpt-oss-120b-chatbot)
|
| 69 |
+
- AWS
|
| 70 |
+
- Deploy via Tensorfuse: [Deploy gpt-oss for both 20b and 120b models on AWS EKS](https://tensorfuse.io/docs/guides/modality/text/openai_oss)
|
| 71 |
+
- [AWS launch blog post](https://aws.amazon.com/blogs/aws/openai-open-weight-models-now-available-on-aws/)
|
| 72 |
+
|
| 73 |
+
## Examples & Tutorials
|
| 74 |
+
|
| 75 |
+
- [OpenAI harmony response format](https://cookbook.openai.com/articles/openai-harmony)
|
| 76 |
+
|
| 77 |
+
## Tools
|
| 78 |
+
|
| 79 |
+
- [Example `python` tool for gpt-oss](./gpt_oss/tools/python_docker/)
|
| 80 |
+
- [Example `browser` tool for gpt-oss](./gpt_oss/tools/simple_browser/)
|
| 81 |
+
|
| 82 |
+
## Training
|
| 83 |
+
|
| 84 |
+
- [Hugging Face TRL examples](https://github.com/huggingface/gpt-oss-recipes)
|
| 85 |
+
- [LlamaFactory examples](https://llamafactory.readthedocs.io/en/latest/advanced/best_practice/gpt-oss.html)
|
| 86 |
+
- [Unsloth examples](https://docs.unsloth.ai/basics/gpt-oss-how-to-run-and-fine-tune)
|
| 87 |
+
|
| 88 |
+
## Contributing
|
| 89 |
+
|
| 90 |
+
Feel free to open a PR to add your own guides and resources on how to run gpt-oss. We will try to review it and add it here.
|
compatibility-test/.gitignore
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Logs
|
| 2 |
+
logs
|
| 3 |
+
*.log
|
| 4 |
+
npm-debug.log*
|
| 5 |
+
yarn-debug.log*
|
| 6 |
+
yarn-error.log*
|
| 7 |
+
lerna-debug.log*
|
| 8 |
+
|
| 9 |
+
# Diagnostic reports (https://nodejs.org/api/report.html)
|
| 10 |
+
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
| 11 |
+
|
| 12 |
+
# Runtime data
|
| 13 |
+
pids
|
| 14 |
+
*.pid
|
| 15 |
+
*.seed
|
| 16 |
+
*.pid.lock
|
| 17 |
+
|
| 18 |
+
# Directory for instrumented libs generated by jscoverage/JSCover
|
| 19 |
+
lib-cov
|
| 20 |
+
|
| 21 |
+
# Coverage directory used by tools like istanbul
|
| 22 |
+
coverage
|
| 23 |
+
*.lcov
|
| 24 |
+
|
| 25 |
+
# nyc test coverage
|
| 26 |
+
.nyc_output
|
| 27 |
+
|
| 28 |
+
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
| 29 |
+
.grunt
|
| 30 |
+
|
| 31 |
+
# Bower dependency directory (https://bower.io/)
|
| 32 |
+
bower_components
|
| 33 |
+
|
| 34 |
+
# node-waf configuration
|
| 35 |
+
.lock-wscript
|
| 36 |
+
|
| 37 |
+
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
| 38 |
+
build/Release
|
| 39 |
+
|
| 40 |
+
# Dependency directories
|
| 41 |
+
node_modules/
|
| 42 |
+
jspm_packages/
|
| 43 |
+
|
| 44 |
+
# Snowpack dependency directory (https://snowpack.dev/)
|
| 45 |
+
web_modules/
|
| 46 |
+
|
| 47 |
+
# TypeScript cache
|
| 48 |
+
*.tsbuildinfo
|
| 49 |
+
|
| 50 |
+
# Optional npm cache directory
|
| 51 |
+
.npm
|
| 52 |
+
|
| 53 |
+
# Optional eslint cache
|
| 54 |
+
.eslintcache
|
| 55 |
+
|
| 56 |
+
# Optional stylelint cache
|
| 57 |
+
.stylelintcache
|
| 58 |
+
|
| 59 |
+
# Optional REPL history
|
| 60 |
+
.node_repl_history
|
| 61 |
+
|
| 62 |
+
# Output of 'npm pack'
|
| 63 |
+
*.tgz
|
| 64 |
+
|
| 65 |
+
# Yarn Integrity file
|
| 66 |
+
.yarn-integrity
|
| 67 |
+
|
| 68 |
+
# dotenv environment variable files
|
| 69 |
+
.env
|
| 70 |
+
.env.*
|
| 71 |
+
!.env.example
|
| 72 |
+
|
| 73 |
+
# parcel-bundler cache (https://parceljs.org/)
|
| 74 |
+
.cache
|
| 75 |
+
.parcel-cache
|
| 76 |
+
|
| 77 |
+
# Next.js build output
|
| 78 |
+
.next
|
| 79 |
+
out
|
| 80 |
+
|
| 81 |
+
# Nuxt.js build / generate output
|
| 82 |
+
.nuxt
|
| 83 |
+
dist
|
| 84 |
+
|
| 85 |
+
# Gatsby files
|
| 86 |
+
.cache/
|
| 87 |
+
# Comment in the public line in if your project uses Gatsby and not Next.js
|
| 88 |
+
# https://nextjs.org/blog/next-9-1#public-directory-support
|
| 89 |
+
# public
|
| 90 |
+
|
| 91 |
+
# vuepress build output
|
| 92 |
+
.vuepress/dist
|
| 93 |
+
|
| 94 |
+
# vuepress v2.x temp and cache directory
|
| 95 |
+
.temp
|
| 96 |
+
.cache
|
| 97 |
+
|
| 98 |
+
# Sveltekit cache directory
|
| 99 |
+
.svelte-kit/
|
| 100 |
+
|
| 101 |
+
# vitepress build output
|
| 102 |
+
**/.vitepress/dist
|
| 103 |
+
|
| 104 |
+
# vitepress cache directory
|
| 105 |
+
**/.vitepress/cache
|
| 106 |
+
|
| 107 |
+
# Docusaurus cache and generated files
|
| 108 |
+
.docusaurus
|
| 109 |
+
|
| 110 |
+
# Serverless directories
|
| 111 |
+
.serverless/
|
| 112 |
+
|
| 113 |
+
# FuseBox cache
|
| 114 |
+
.fusebox/
|
| 115 |
+
|
| 116 |
+
# DynamoDB Local files
|
| 117 |
+
.dynamodb/
|
| 118 |
+
|
| 119 |
+
# Firebase cache directory
|
| 120 |
+
.firebase/
|
| 121 |
+
|
| 122 |
+
# TernJS port file
|
| 123 |
+
.tern-port
|
| 124 |
+
|
| 125 |
+
# Stores VSCode versions used for testing VSCode extensions
|
| 126 |
+
.vscode-test
|
| 127 |
+
|
| 128 |
+
# yarn v3
|
| 129 |
+
.pnp.*
|
| 130 |
+
.yarn/*
|
| 131 |
+
!.yarn/patches
|
| 132 |
+
!.yarn/plugins
|
| 133 |
+
!.yarn/releases
|
| 134 |
+
!.yarn/sdks
|
| 135 |
+
!.yarn/versions
|
| 136 |
+
|
| 137 |
+
# Vite logs files
|
| 138 |
+
vite.config.js.timestamp-*
|
| 139 |
+
vite.config.ts.timestamp-*
|
| 140 |
+
|
| 141 |
+
rollout_*.jsonl
|
| 142 |
+
analysis_*.json
|
compatibility-test/README.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# API Compatibility Test
|
| 2 |
+
|
| 3 |
+
This script uses the Agents SDK in TypeScript and the underlying OpenAI client to verify the shape of the API calls but also whether the API performs tool calling.
|
| 4 |
+
|
| 5 |
+
## What it tests
|
| 6 |
+
|
| 7 |
+
1.
|
| 8 |
+
|
| 9 |
+
## How to run
|
| 10 |
+
|
| 11 |
+
0. Run `npm install` in this directory.
|
| 12 |
+
1. Update `providers.ts` to create an entry for the API to test. Change `vllm` to the provider name of your choice. Use `chat` for Chat Completions tests and `responses` for Responses API tests.
|
| 13 |
+
2. Run an initial quick test to make sure things work. This will only run one test
|
| 14 |
+
|
| 15 |
+
```
|
| 16 |
+
npm start -- --provider <name> -n 1 -k 1
|
| 17 |
+
```
|
| 18 |
+
|
| 19 |
+
3. Run the full test (runs each test 5 times to test consistency)
|
| 20 |
+
|
| 21 |
+
```
|
| 22 |
+
npm start -- --provider <name> -k 5
|
| 23 |
+
```
|
| 24 |
+
|
| 25 |
+
## Considerations
|
| 26 |
+
|
| 27 |
+
1. The tests will fail if the API shape does not match the expected behavior
|
| 28 |
+
2. Events in the chat API are currently not tested
|
| 29 |
+
3. If the schema validation succeeds but the input is wrong the test will still pass for this test. That's because it's likely more of a prompt engineering issue or a validator issue than an API issue as it still nailed the input
|
compatibility-test/analysis.ts
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export function analyze(caseResults: any[], tries: number) {
|
| 2 |
+
// Group results by unique task: test_case + apiType
|
| 3 |
+
type TaskKey = string;
|
| 4 |
+
const taskKeyFor = (r: any): TaskKey =>
|
| 5 |
+
`${r.test_case}::${r.result?.apiType}`;
|
| 6 |
+
|
| 7 |
+
const successesByTask: Map<TaskKey, Map<number, boolean>> = new Map();
|
| 8 |
+
|
| 9 |
+
// Count wrong-input tool calls (schema correct but incorrect arguments)
|
| 10 |
+
let wrongInputToolCalls = 0;
|
| 11 |
+
|
| 12 |
+
// Count invalid response shapes per API type
|
| 13 |
+
const totalByApiType: Record<string, number> = {};
|
| 14 |
+
const invalidByApiType: Record<string, number> = {};
|
| 15 |
+
|
| 16 |
+
for (const r of caseResults) {
|
| 17 |
+
if (!r?.result || typeof r.result.apiType !== "string") continue;
|
| 18 |
+
|
| 19 |
+
// Parse attempt index from run_id `${i}_${k}` safely
|
| 20 |
+
let attemptIndex: number | undefined;
|
| 21 |
+
if (typeof r.run_id === "string") {
|
| 22 |
+
const parts = r.run_id.split("_");
|
| 23 |
+
const k = Number(parts[1]);
|
| 24 |
+
if (Number.isFinite(k)) attemptIndex = k;
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
const key = taskKeyFor(r);
|
| 28 |
+
if (!successesByTask.has(key)) successesByTask.set(key, new Map());
|
| 29 |
+
if (attemptIndex != null) {
|
| 30 |
+
successesByTask.get(key)!.set(attemptIndex, Boolean(r.success));
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
const d = r.result.toolCallingDetails ?? {};
|
| 34 |
+
const calledToolAtLeastOnce = Boolean(d.calledToolAtLeastOnce);
|
| 35 |
+
const calledToolWithRightSchema = Boolean(d.calledToolWithRightSchema);
|
| 36 |
+
const calledToolWithRightArguments = Boolean(
|
| 37 |
+
d.calledToolWithRightArguments
|
| 38 |
+
);
|
| 39 |
+
if (
|
| 40 |
+
calledToolAtLeastOnce &&
|
| 41 |
+
calledToolWithRightSchema &&
|
| 42 |
+
!calledToolWithRightArguments
|
| 43 |
+
) {
|
| 44 |
+
wrongInputToolCalls++;
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
// Track invalid/total per apiType for response shape
|
| 48 |
+
const apiType = r.result.apiType as string;
|
| 49 |
+
totalByApiType[apiType] = (totalByApiType[apiType] ?? 0) + 1;
|
| 50 |
+
const isValidResponse = r.result.validResponse === true;
|
| 51 |
+
if (!isValidResponse) {
|
| 52 |
+
invalidByApiType[apiType] = (invalidByApiType[apiType] ?? 0) + 1;
|
| 53 |
+
}
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
const totalTasks = successesByTask.size;
|
| 57 |
+
|
| 58 |
+
// Compute pass@k and pass^k for k = 1..tries
|
| 59 |
+
const passAtKByK: number[] = [];
|
| 60 |
+
const passHatKByK: number[] = [];
|
| 61 |
+
|
| 62 |
+
for (let k = 1; k <= tries; k++) {
|
| 63 |
+
let tasksSuccessfulK = 0; // any success in first k attempts
|
| 64 |
+
let tasksAllSuccessfulK = 0; // all success in first k attempts
|
| 65 |
+
|
| 66 |
+
for (const [, attemptsMap] of successesByTask) {
|
| 67 |
+
let anySuccess = false;
|
| 68 |
+
let allSuccess = true;
|
| 69 |
+
for (let i = 0; i < k; i++) {
|
| 70 |
+
const v = attemptsMap.get(i) === true;
|
| 71 |
+
anySuccess = anySuccess || v;
|
| 72 |
+
if (!v) allSuccess = false;
|
| 73 |
+
}
|
| 74 |
+
if (anySuccess) tasksSuccessfulK++;
|
| 75 |
+
if (allSuccess) tasksAllSuccessfulK++;
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
const passAtK = totalTasks > 0 ? tasksSuccessfulK / totalTasks : 0;
|
| 79 |
+
const passHatK = totalTasks > 0 ? tasksAllSuccessfulK / totalTasks : 0;
|
| 80 |
+
passAtKByK.push(passAtK);
|
| 81 |
+
passHatKByK.push(passHatK);
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
// Convenience: final k=tries values
|
| 85 |
+
const passAtK = passAtKByK[tries - 1] ?? 0;
|
| 86 |
+
const passHatK = passHatKByK[tries - 1] ?? 0;
|
| 87 |
+
|
| 88 |
+
return {
|
| 89 |
+
totalTasks,
|
| 90 |
+
passAtKByK,
|
| 91 |
+
passHatKByK,
|
| 92 |
+
passAtK,
|
| 93 |
+
passHatK,
|
| 94 |
+
wrongInputToolCalls,
|
| 95 |
+
// New stats for invalid response shapes per API
|
| 96 |
+
invalidByApiType,
|
| 97 |
+
totalByApiType,
|
| 98 |
+
};
|
| 99 |
+
}
|
| 100 |
+
|
| 101 |
+
export function printAnalysis(
|
| 102 |
+
stats: ReturnType<typeof analyze>,
|
| 103 |
+
caseResults: any[],
|
| 104 |
+
provider: string,
|
| 105 |
+
selectedLines: string[],
|
| 106 |
+
tries: number,
|
| 107 |
+
skipped: number,
|
| 108 |
+
analysisFile: string
|
| 109 |
+
) {
|
| 110 |
+
const formatPerK = (arr: number[]) =>
|
| 111 |
+
Array.from({ length: tries }, (_, i) => {
|
| 112 |
+
const v = arr[i] ?? 0;
|
| 113 |
+
return `${i + 1}=${v.toFixed(3)}`;
|
| 114 |
+
}).join(", ");
|
| 115 |
+
|
| 116 |
+
console.log("Summary:");
|
| 117 |
+
console.log(` Provider: ${provider}`);
|
| 118 |
+
console.log(` Total input cases: ${selectedLines.length}`);
|
| 119 |
+
console.log(` Tries: ${tries}`);
|
| 120 |
+
console.log(` Total tasks: ${stats.totalTasks}`);
|
| 121 |
+
console.log(` Total runs: ${caseResults.length}`);
|
| 122 |
+
// Conditionally print invalid response shape stats per API type
|
| 123 |
+
if ((stats.totalByApiType["responses"] ?? 0) > 0) {
|
| 124 |
+
const bad = stats.invalidByApiType["responses"] ?? 0;
|
| 125 |
+
const tot = stats.totalByApiType["responses"] ?? 0;
|
| 126 |
+
console.log(` Invalid Responses API responses: ${bad} (out of ${tot})`);
|
| 127 |
+
}
|
| 128 |
+
if ((stats.totalByApiType["chat"] ?? 0) > 0) {
|
| 129 |
+
const bad = stats.invalidByApiType["chat"] ?? 0;
|
| 130 |
+
const tot = stats.totalByApiType["chat"] ?? 0;
|
| 131 |
+
console.log(
|
| 132 |
+
` Invalid Chat Completions API responses: ${bad} (out of ${tot})`
|
| 133 |
+
);
|
| 134 |
+
}
|
| 135 |
+
console.log(` pass@k (k=1..${tries}): ${formatPerK(stats.passAtKByK)}`);
|
| 136 |
+
console.log(` pass^k (k=1..${tries}): ${formatPerK(stats.passHatKByK)}`);
|
| 137 |
+
console.log(` pass@k (k=${tries}): ${stats.passAtK.toFixed(3)}`);
|
| 138 |
+
console.log(` pass^k (k=${tries}): ${stats.passHatK.toFixed(3)}`);
|
| 139 |
+
console.log(` Wrong-input tool calls: ${stats.wrongInputToolCalls}`);
|
| 140 |
+
console.log(` Invalid cases.jsonl lines: ${skipped}`);
|
| 141 |
+
console.log(` Analysis written to ${analysisFile}`);
|
| 142 |
+
}
|
compatibility-test/cases.jsonl
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{"tool_name":"get_system_health","input":"Hey, quick check: is everything up and running?","expected_arguments":"{}"}
|
| 2 |
+
{"tool_name":"get_system_health","input":"Status report please.","expected_arguments":"{}"}
|
| 3 |
+
{"tool_name":"get_system_health","input":"Can you confirm the LLM health before we start?","expected_arguments":"{}"}
|
| 4 |
+
{"tool_name":"get_system_health","input":"Need a health snapshot.","expected_arguments":"{}"}
|
| 5 |
+
{"tool_name":"get_system_health","input":"Hi, what's the current system health?","expected_arguments":"{}"}
|
| 6 |
+
{"tool_name":"markdown_to_html","input":"Convert this markdown to HTML:\n\n# Title\n\nSome *italic* text.","expected_arguments":"{\"markdown\":\"# Title\\n\\nSome *italic* text.\"}"}
|
| 7 |
+
{"tool_name":"markdown_to_html","input":"Hey, could you turn `## Docs` into HTML?","expected_arguments":"{\"markdown\":\"## Docs\"}"}
|
| 8 |
+
{"tool_name":"markdown_to_html","input":"Please render the following markdown:\n\n- item 1\n- item 2","expected_arguments":"{\"markdown\":\"- item 1\\n- item 2\"}"}
|
| 9 |
+
{"tool_name":"markdown_to_html","input":"I have `**bold**` markdown; give me HTML.","expected_arguments":"{\"markdown\":\"**bold**\"}"}
|
| 10 |
+
{"tool_name":"markdown_to_html","input":"Markdown to HTML: > quote","expected_arguments":"{\"markdown\":\"> quote\"}"}
|
| 11 |
+
{"tool_name":"detect_language","input":"Hey, what language is this: 'Buenos días, ¿cómo estás?'","expected_arguments":"{\"text\":\"Buenos días, ¿cómo estás?\"}"}
|
| 12 |
+
{"tool_name":"detect_language","input":"Identify the language: \"Guten Morgen\"","expected_arguments":"{\"text\":\"Guten Morgen\"}"}
|
| 13 |
+
{"tool_name":"detect_language","input":"Language detection needed: こんにちは、お元気ですか?","expected_arguments":"{\"text\":\"こんにちは、お元気ですか?\"}"}
|
| 14 |
+
{"tool_name":"detect_language","input":"Detect language for: 'Привет, как дела?'","expected_arguments":"{\"text\":\"Привет, как дела?\"}"}
|
| 15 |
+
{"tool_name":"detect_language","input":"What language is 'Bonjour tout le monde'?","expected_arguments":"{\"text\":\"Bonjour tout le monde\"}"}
|
| 16 |
+
{"tool_name":"generate_chart","input":"Plot a simple line chart for these points: (1,2),(2,4),(3,9).","expected_arguments":"{\"data\":[[1,2],[2,4],[3,9]],\"chart_type\":\"line\"}"}
|
| 17 |
+
{"tool_name":"generate_chart","input":"Hey, can I get a bar chart of my sales: 10, 20, 30 across Q1–Q3?","expected_arguments":"{\"data\":[[1,10],[2,20],[3,30]],\"chart_type\":\"bar\",\"title\":\"Quarterly Sales\"}"}
|
| 18 |
+
{"tool_name":"generate_chart","input":"Make a scatter chart titled 'Experiment' with x label Time and y label Value for data [ [0,1], [1,1.5], [2,2.2] ].","expected_arguments":"{\"data\":[[0,1],[1,1.5],[2,2.2]],\"chart_type\":\"scatter\",\"title\":\"Experiment\",\"x_label\":\"Time\",\"y_label\":\"Value\"}"}
|
| 19 |
+
{"tool_name":"generate_chart","input":"Create a line chart of temperatures 70,72,68,65 over 4 days, label x as 'Day'.","expected_arguments":"{\"data\":[[1,70],[2,72],[3,68],[4,65]],\"chart_type\":\"line\",\"x_label\":\"Day\"}"}
|
| 20 |
+
{"tool_name":"generate_chart","input":"Visualize visits per day with a bar chart; numbers: 100,150,120.","expected_arguments":"{\"data\":[[1,100],[2,150],[3,120]],\"chart_type\":\"bar\",\"title\":\"Daily Visits\",\"y_label\":\"Visitors\"}"}
|
| 21 |
+
{"tool_name":"query_database","input":"Give me the ids and emails from users table, limit 5.","expected_arguments":"{\"table\":\"users\",\"columns\":[\"id\",\"email\"],\"limit\":5}"}
|
| 22 |
+
{"tool_name":"query_database","input":"Hey, fetch order_id and amount from orders where status is 'shipped'.","expected_arguments":"{\"table\":\"orders\",\"columns\":[\"order_id\",\"amount\"],\"filters\":\"status = 'shipped'\"}"}
|
| 23 |
+
{"tool_name":"query_database","input":"Retrieve name and price from products ordered by price descending, top 10 please.","expected_arguments":"{\"table\":\"products\",\"columns\":[\"name\",\"price\"],\"limit\":10,\"order_by\":\"price DESC\"}"}
|
| 24 |
+
{"tool_name":"query_database","input":"I need the first 3 log entries from audit_log table.","expected_arguments":"{\"table\":\"audit_log\",\"columns\":[\"id\",\"timestamp\",\"action\"],\"limit\":3}"}
|
| 25 |
+
{"tool_name":"query_database","input":"Query the customers table for name, city where city = 'Berlin'.","expected_arguments":"{\"table\":\"customers\",\"columns\":[\"name\",\"city\"],\"filters\":\"city = 'Berlin'\"}"}
|
| 26 |
+
{"tool_name":"get_weather","input":"What's the weather in San Francisco right now?","expected_arguments":"{\"location\":\"San Francisco\"}"}
|
| 27 |
+
{"tool_name":"get_weather","input":"Weather for Tokyo, please.","expected_arguments":"{\"location\":\"Tokyo\"}"}
|
| 28 |
+
{"tool_name":"get_weather","input":"Get me the current weather for 10001.","expected_arguments":"{\"location\":\"10001\"}"}
|
| 29 |
+
{"tool_name":"get_weather","input":"How's the weather in Paris today?","expected_arguments":"{\"location\":\"Paris\"}"}
|
| 30 |
+
{"tool_name":"get_weather","input":"Check the weather for Sydney.","expected_arguments":"{\"location\":\"Sydney\"}"}
|
compatibility-test/index.ts
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { parseArgs } from "node:util";
|
| 2 |
+
import { createWriteStream } from "node:fs";
|
| 3 |
+
import { readFile, writeFile } from "node:fs/promises";
|
| 4 |
+
import path from "node:path";
|
| 5 |
+
import process from "node:process";
|
| 6 |
+
import { runCase, RunCaseSummary } from "./runCase";
|
| 7 |
+
import { Listr, ListrTaskWrapper } from "listr2";
|
| 8 |
+
import { analyze, printAnalysis } from "./analysis";
|
| 9 |
+
|
| 10 |
+
function formatTimestamp(d: Date): string {
|
| 11 |
+
const pad = (n: number) => String(n).padStart(2, "0");
|
| 12 |
+
const yyyy = d.getFullYear();
|
| 13 |
+
const mm = pad(d.getMonth() + 1);
|
| 14 |
+
const dd = pad(d.getDate());
|
| 15 |
+
const hh = pad(d.getHours());
|
| 16 |
+
const mi = pad(d.getMinutes());
|
| 17 |
+
const ss = pad(d.getSeconds());
|
| 18 |
+
return `${yyyy}${mm}${dd}_${hh}${mi}${ss}`;
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
async function main() {
|
| 22 |
+
const args = parseArgs({
|
| 23 |
+
options: {
|
| 24 |
+
cases: { type: "string", short: "c", default: "cases.jsonl" },
|
| 25 |
+
provider: { type: "string", short: "p", default: "openai" },
|
| 26 |
+
streaming: { type: "boolean", short: "s", default: false },
|
| 27 |
+
maxTurns: { type: "string", short: "t", default: "10" },
|
| 28 |
+
n: { type: "string", short: "n" },
|
| 29 |
+
strict: { type: "boolean", short: "s", default: false },
|
| 30 |
+
tries: { type: "string", short: "k", default: "1" },
|
| 31 |
+
},
|
| 32 |
+
});
|
| 33 |
+
const casesPathArg = args.values.cases;
|
| 34 |
+
const provider = args.values.provider as string;
|
| 35 |
+
const streaming = Boolean(args.values.streaming);
|
| 36 |
+
const maxTurns = Number(args.values.maxTurns ?? 10);
|
| 37 |
+
const nRaw = args.values.n as string | undefined;
|
| 38 |
+
const triesRaw = args.values.tries as string | undefined;
|
| 39 |
+
const tries = triesRaw != null ? Number(triesRaw) : 1;
|
| 40 |
+
const limit = nRaw != null ? Number(nRaw) : undefined;
|
| 41 |
+
if (limit != null && (!Number.isFinite(limit) || limit <= 0)) {
|
| 42 |
+
console.error("--n must be a positive integer");
|
| 43 |
+
process.exitCode = 1;
|
| 44 |
+
return;
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
if (!casesPathArg) {
|
| 48 |
+
console.error("--cases is required (path to JSONL file)");
|
| 49 |
+
process.exitCode = 1;
|
| 50 |
+
return;
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
const casesPath = path.isAbsolute(casesPathArg)
|
| 54 |
+
? casesPathArg
|
| 55 |
+
: path.join(process.cwd(), casesPathArg);
|
| 56 |
+
|
| 57 |
+
const timestamp = formatTimestamp(new Date());
|
| 58 |
+
const defaultFilename = `rollout_${provider}_${timestamp}.jsonl`;
|
| 59 |
+
const outputFile = path.join(process.cwd(), defaultFilename);
|
| 60 |
+
const analysisFile = path.join(
|
| 61 |
+
process.cwd(),
|
| 62 |
+
`analysis_${provider}_${timestamp}.json`
|
| 63 |
+
);
|
| 64 |
+
|
| 65 |
+
let fileContent: string;
|
| 66 |
+
try {
|
| 67 |
+
fileContent = await readFile(casesPath, "utf8");
|
| 68 |
+
} catch (err: any) {
|
| 69 |
+
console.error(
|
| 70 |
+
`Failed to read cases file at ${casesPath}: ${err?.message ?? err}`
|
| 71 |
+
);
|
| 72 |
+
process.exitCode = 1;
|
| 73 |
+
return;
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
const lines = fileContent
|
| 77 |
+
.split(/\r?\n/)
|
| 78 |
+
.map((l) => l.trim())
|
| 79 |
+
.filter((l) => l.length > 0);
|
| 80 |
+
|
| 81 |
+
const selectedLines =
|
| 82 |
+
typeof limit === "number" ? lines.slice(0, limit) : lines;
|
| 83 |
+
|
| 84 |
+
const out = createWriteStream(outputFile, { flags: "w", encoding: "utf8" });
|
| 85 |
+
|
| 86 |
+
const writeLine = (obj: any) =>
|
| 87 |
+
new Promise<void>((resolve, reject) => {
|
| 88 |
+
const str = JSON.stringify(obj) + "\n";
|
| 89 |
+
out.write(str, (err) => (err ? reject(err) : resolve()));
|
| 90 |
+
});
|
| 91 |
+
|
| 92 |
+
// Accumulators for post-run analysis
|
| 93 |
+
let skipped = 0; // invalid JSON lines
|
| 94 |
+
const caseResults: Array<{
|
| 95 |
+
run_id: string;
|
| 96 |
+
success: boolean;
|
| 97 |
+
provider: string;
|
| 98 |
+
test_case: number;
|
| 99 |
+
tool_name: string;
|
| 100 |
+
input: string;
|
| 101 |
+
result: RunCaseSummary;
|
| 102 |
+
}> = [];
|
| 103 |
+
|
| 104 |
+
async function processIndex(
|
| 105 |
+
i: number,
|
| 106 |
+
k: number,
|
| 107 |
+
task: ListrTaskWrapper<any, any, any>
|
| 108 |
+
) {
|
| 109 |
+
const line = selectedLines[i];
|
| 110 |
+
let caseObj: any;
|
| 111 |
+
try {
|
| 112 |
+
caseObj = JSON.parse(line);
|
| 113 |
+
} catch (err: any) {
|
| 114 |
+
console.error(
|
| 115 |
+
`Skipping invalid JSON on line ${i + 1}: ${err?.message ?? err}`
|
| 116 |
+
);
|
| 117 |
+
skipped++;
|
| 118 |
+
return;
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
try {
|
| 122 |
+
const summaries = await runCase(provider, caseObj, {
|
| 123 |
+
maxTurns,
|
| 124 |
+
streaming,
|
| 125 |
+
strict: args.values.strict,
|
| 126 |
+
});
|
| 127 |
+
|
| 128 |
+
for (const summary of summaries) {
|
| 129 |
+
const record = {
|
| 130 |
+
run_id: `${i}_${k}`,
|
| 131 |
+
success: summary.success,
|
| 132 |
+
provider,
|
| 133 |
+
test_case: i,
|
| 134 |
+
tool_name: caseObj.tool_name,
|
| 135 |
+
input: caseObj.input,
|
| 136 |
+
result: summary,
|
| 137 |
+
};
|
| 138 |
+
task.output = `Case ${i} (attempt ${k + 1}): ${
|
| 139 |
+
summary.success ? "Success" : "Failed"
|
| 140 |
+
} ${summary.toolCallingDetails.warning || ""}`;
|
| 141 |
+
caseResults.push(record);
|
| 142 |
+
await writeLine(record);
|
| 143 |
+
}
|
| 144 |
+
} catch (err: any) {
|
| 145 |
+
const record = {
|
| 146 |
+
provider,
|
| 147 |
+
test_case: i,
|
| 148 |
+
tool_name: caseObj?.tool_name,
|
| 149 |
+
input: caseObj?.input,
|
| 150 |
+
expected_output: caseObj?.expected_output,
|
| 151 |
+
instructions: caseObj?.instructions,
|
| 152 |
+
error: String(err?.message ?? err),
|
| 153 |
+
};
|
| 154 |
+
await writeLine(record);
|
| 155 |
+
task.output = `Case ${i} failed: ${err?.message ?? err}`;
|
| 156 |
+
}
|
| 157 |
+
}
|
| 158 |
+
|
| 159 |
+
const listr = new Listr<{
|
| 160 |
+
output: string;
|
| 161 |
+
}>(
|
| 162 |
+
selectedLines.flatMap((line, index) => {
|
| 163 |
+
return Array.from({ length: tries }, (_, attempt) => ({
|
| 164 |
+
title: `Processing case ${index} (attempt ${attempt + 1})`,
|
| 165 |
+
task: async (_, task) => {
|
| 166 |
+
await processIndex(index, attempt, task);
|
| 167 |
+
},
|
| 168 |
+
rendererOptions: { persistentOutput: true },
|
| 169 |
+
}));
|
| 170 |
+
}),
|
| 171 |
+
{
|
| 172 |
+
concurrent: 5,
|
| 173 |
+
}
|
| 174 |
+
);
|
| 175 |
+
|
| 176 |
+
await listr.run();
|
| 177 |
+
|
| 178 |
+
await new Promise((resolve) => out.end(resolve));
|
| 179 |
+
console.log(`Results written to ${outputFile}`);
|
| 180 |
+
const stats = analyze(caseResults, tries);
|
| 181 |
+
await writeFile(analysisFile, JSON.stringify(stats, null, 2), "utf8");
|
| 182 |
+
printAnalysis(
|
| 183 |
+
stats,
|
| 184 |
+
caseResults,
|
| 185 |
+
provider,
|
| 186 |
+
selectedLines,
|
| 187 |
+
tries,
|
| 188 |
+
skipped,
|
| 189 |
+
analysisFile
|
| 190 |
+
);
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
main().catch((err) => {
|
| 194 |
+
console.error(err);
|
| 195 |
+
process.exitCode = 1;
|
| 196 |
+
});
|
compatibility-test/package-lock.json
ADDED
|
@@ -0,0 +1,1633 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "compatibility-test",
|
| 3 |
+
"lockfileVersion": 3,
|
| 4 |
+
"requires": true,
|
| 5 |
+
"packages": {
|
| 6 |
+
"": {
|
| 7 |
+
"dependencies": {
|
| 8 |
+
"@openai/agents": "^0.0.15",
|
| 9 |
+
"ajv": "^8.17.1",
|
| 10 |
+
"listr2": "^9.0.1"
|
| 11 |
+
}
|
| 12 |
+
},
|
| 13 |
+
"node_modules/@modelcontextprotocol/sdk": {
|
| 14 |
+
"version": "1.17.1",
|
| 15 |
+
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.17.1.tgz",
|
| 16 |
+
"integrity": "sha512-CPle1OQehbWqd25La9Ack5B07StKIxh4+Bf19qnpZKJC1oI22Y0czZHbifjw1UoczIfKBwBDAp/dFxvHG13B5A==",
|
| 17 |
+
"license": "MIT",
|
| 18 |
+
"optional": true,
|
| 19 |
+
"dependencies": {
|
| 20 |
+
"ajv": "^6.12.6",
|
| 21 |
+
"content-type": "^1.0.5",
|
| 22 |
+
"cors": "^2.8.5",
|
| 23 |
+
"cross-spawn": "^7.0.5",
|
| 24 |
+
"eventsource": "^3.0.2",
|
| 25 |
+
"eventsource-parser": "^3.0.0",
|
| 26 |
+
"express": "^5.0.1",
|
| 27 |
+
"express-rate-limit": "^7.5.0",
|
| 28 |
+
"pkce-challenge": "^5.0.0",
|
| 29 |
+
"raw-body": "^3.0.0",
|
| 30 |
+
"zod": "^3.23.8",
|
| 31 |
+
"zod-to-json-schema": "^3.24.1"
|
| 32 |
+
},
|
| 33 |
+
"engines": {
|
| 34 |
+
"node": ">=18"
|
| 35 |
+
}
|
| 36 |
+
},
|
| 37 |
+
"node_modules/@modelcontextprotocol/sdk/node_modules/ajv": {
|
| 38 |
+
"version": "6.12.6",
|
| 39 |
+
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
| 40 |
+
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
| 41 |
+
"license": "MIT",
|
| 42 |
+
"optional": true,
|
| 43 |
+
"dependencies": {
|
| 44 |
+
"fast-deep-equal": "^3.1.1",
|
| 45 |
+
"fast-json-stable-stringify": "^2.0.0",
|
| 46 |
+
"json-schema-traverse": "^0.4.1",
|
| 47 |
+
"uri-js": "^4.2.2"
|
| 48 |
+
},
|
| 49 |
+
"funding": {
|
| 50 |
+
"type": "github",
|
| 51 |
+
"url": "https://github.com/sponsors/epoberezkin"
|
| 52 |
+
}
|
| 53 |
+
},
|
| 54 |
+
"node_modules/@modelcontextprotocol/sdk/node_modules/json-schema-traverse": {
|
| 55 |
+
"version": "0.4.1",
|
| 56 |
+
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
| 57 |
+
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
| 58 |
+
"license": "MIT",
|
| 59 |
+
"optional": true
|
| 60 |
+
},
|
| 61 |
+
"node_modules/@openai/agents": {
|
| 62 |
+
"version": "0.0.15",
|
| 63 |
+
"resolved": "https://registry.npmjs.org/@openai/agents/-/agents-0.0.15.tgz",
|
| 64 |
+
"integrity": "sha512-B8y+WyWOeHowflPx09pyCfcqikC4OYWK27HTyNGt1oraXv93CzuamSr76iAaU1nWQ1MPbUwl6LHPX4BPUikVkQ==",
|
| 65 |
+
"license": "MIT",
|
| 66 |
+
"dependencies": {
|
| 67 |
+
"@openai/agents-core": "0.0.15",
|
| 68 |
+
"@openai/agents-openai": "0.0.15",
|
| 69 |
+
"@openai/agents-realtime": "0.0.15",
|
| 70 |
+
"debug": "^4.4.0",
|
| 71 |
+
"openai": "^5.10.1"
|
| 72 |
+
}
|
| 73 |
+
},
|
| 74 |
+
"node_modules/@openai/agents-core": {
|
| 75 |
+
"version": "0.0.15",
|
| 76 |
+
"resolved": "https://registry.npmjs.org/@openai/agents-core/-/agents-core-0.0.15.tgz",
|
| 77 |
+
"integrity": "sha512-ODTqttjW0s0ejBe5PKnYRlFbJSZH2IO6OtUlRhIKmWiWrX6pGRxvpKjTSOXy8DEtpRHBj6Nhky0UoSlO6eOkDQ==",
|
| 78 |
+
"license": "MIT",
|
| 79 |
+
"dependencies": {
|
| 80 |
+
"@openai/zod": "npm:zod@3.25.40 - 3.25.67",
|
| 81 |
+
"debug": "^4.4.0",
|
| 82 |
+
"openai": "^5.10.1"
|
| 83 |
+
},
|
| 84 |
+
"optionalDependencies": {
|
| 85 |
+
"@modelcontextprotocol/sdk": "^1.12.0"
|
| 86 |
+
},
|
| 87 |
+
"peerDependencies": {
|
| 88 |
+
"zod": "3.25.40 - 3.25.67"
|
| 89 |
+
},
|
| 90 |
+
"peerDependenciesMeta": {
|
| 91 |
+
"zod": {
|
| 92 |
+
"optional": true
|
| 93 |
+
}
|
| 94 |
+
}
|
| 95 |
+
},
|
| 96 |
+
"node_modules/@openai/agents-openai": {
|
| 97 |
+
"version": "0.0.15",
|
| 98 |
+
"resolved": "https://registry.npmjs.org/@openai/agents-openai/-/agents-openai-0.0.15.tgz",
|
| 99 |
+
"integrity": "sha512-YIX3n98HdmmWKkb/71OB+DCQUYyGEpqfzPjejzdtNLUvAEs3jvXf7nkC8oTISsuCwrirgBz0rQEefeo0oUlyFQ==",
|
| 100 |
+
"license": "MIT",
|
| 101 |
+
"dependencies": {
|
| 102 |
+
"@openai/agents-core": "0.0.15",
|
| 103 |
+
"@openai/zod": "npm:zod@3.25.40 - 3.25.67",
|
| 104 |
+
"debug": "^4.4.0",
|
| 105 |
+
"openai": "^5.10.1"
|
| 106 |
+
}
|
| 107 |
+
},
|
| 108 |
+
"node_modules/@openai/agents-realtime": {
|
| 109 |
+
"version": "0.0.15",
|
| 110 |
+
"resolved": "https://registry.npmjs.org/@openai/agents-realtime/-/agents-realtime-0.0.15.tgz",
|
| 111 |
+
"integrity": "sha512-kSZzMyij9Xt3BpMb/9snuVnu7a5qKZLyhtN/kWMA+wmfETvWz23BBz6tbO5xOmurAt9//OktkB+94e0T0RBtlA==",
|
| 112 |
+
"license": "MIT",
|
| 113 |
+
"dependencies": {
|
| 114 |
+
"@openai/agents-core": "0.0.15",
|
| 115 |
+
"@openai/zod": "npm:zod@3.25.40 - 3.25.67",
|
| 116 |
+
"@types/ws": "^8.18.1",
|
| 117 |
+
"debug": "^4.4.0",
|
| 118 |
+
"ws": "^8.18.1"
|
| 119 |
+
}
|
| 120 |
+
},
|
| 121 |
+
"node_modules/@openai/zod": {
|
| 122 |
+
"name": "zod",
|
| 123 |
+
"version": "3.25.67",
|
| 124 |
+
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz",
|
| 125 |
+
"integrity": "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==",
|
| 126 |
+
"license": "MIT",
|
| 127 |
+
"funding": {
|
| 128 |
+
"url": "https://github.com/sponsors/colinhacks"
|
| 129 |
+
}
|
| 130 |
+
},
|
| 131 |
+
"node_modules/@types/node": {
|
| 132 |
+
"version": "24.2.0",
|
| 133 |
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.0.tgz",
|
| 134 |
+
"integrity": "sha512-3xyG3pMCq3oYCNg7/ZP+E1ooTaGB4cG8JWRsqqOYQdbWNY4zbaV0Ennrd7stjiJEFZCaybcIgpTjJWHRfBSIDw==",
|
| 135 |
+
"license": "MIT",
|
| 136 |
+
"dependencies": {
|
| 137 |
+
"undici-types": "~7.10.0"
|
| 138 |
+
}
|
| 139 |
+
},
|
| 140 |
+
"node_modules/@types/ws": {
|
| 141 |
+
"version": "8.18.1",
|
| 142 |
+
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
|
| 143 |
+
"integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
|
| 144 |
+
"license": "MIT",
|
| 145 |
+
"dependencies": {
|
| 146 |
+
"@types/node": "*"
|
| 147 |
+
}
|
| 148 |
+
},
|
| 149 |
+
"node_modules/accepts": {
|
| 150 |
+
"version": "2.0.0",
|
| 151 |
+
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
|
| 152 |
+
"integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
|
| 153 |
+
"license": "MIT",
|
| 154 |
+
"optional": true,
|
| 155 |
+
"dependencies": {
|
| 156 |
+
"mime-types": "^3.0.0",
|
| 157 |
+
"negotiator": "^1.0.0"
|
| 158 |
+
},
|
| 159 |
+
"engines": {
|
| 160 |
+
"node": ">= 0.6"
|
| 161 |
+
}
|
| 162 |
+
},
|
| 163 |
+
"node_modules/ajv": {
|
| 164 |
+
"version": "8.17.1",
|
| 165 |
+
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
| 166 |
+
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
| 167 |
+
"license": "MIT",
|
| 168 |
+
"dependencies": {
|
| 169 |
+
"fast-deep-equal": "^3.1.3",
|
| 170 |
+
"fast-uri": "^3.0.1",
|
| 171 |
+
"json-schema-traverse": "^1.0.0",
|
| 172 |
+
"require-from-string": "^2.0.2"
|
| 173 |
+
},
|
| 174 |
+
"funding": {
|
| 175 |
+
"type": "github",
|
| 176 |
+
"url": "https://github.com/sponsors/epoberezkin"
|
| 177 |
+
}
|
| 178 |
+
},
|
| 179 |
+
"node_modules/ansi-escapes": {
|
| 180 |
+
"version": "7.0.0",
|
| 181 |
+
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz",
|
| 182 |
+
"integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==",
|
| 183 |
+
"license": "MIT",
|
| 184 |
+
"dependencies": {
|
| 185 |
+
"environment": "^1.0.0"
|
| 186 |
+
},
|
| 187 |
+
"engines": {
|
| 188 |
+
"node": ">=18"
|
| 189 |
+
},
|
| 190 |
+
"funding": {
|
| 191 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 192 |
+
}
|
| 193 |
+
},
|
| 194 |
+
"node_modules/ansi-regex": {
|
| 195 |
+
"version": "6.1.0",
|
| 196 |
+
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
|
| 197 |
+
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
|
| 198 |
+
"license": "MIT",
|
| 199 |
+
"engines": {
|
| 200 |
+
"node": ">=12"
|
| 201 |
+
},
|
| 202 |
+
"funding": {
|
| 203 |
+
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
| 204 |
+
}
|
| 205 |
+
},
|
| 206 |
+
"node_modules/ansi-styles": {
|
| 207 |
+
"version": "6.2.1",
|
| 208 |
+
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
|
| 209 |
+
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
|
| 210 |
+
"license": "MIT",
|
| 211 |
+
"engines": {
|
| 212 |
+
"node": ">=12"
|
| 213 |
+
},
|
| 214 |
+
"funding": {
|
| 215 |
+
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
| 216 |
+
}
|
| 217 |
+
},
|
| 218 |
+
"node_modules/body-parser": {
|
| 219 |
+
"version": "2.2.0",
|
| 220 |
+
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
|
| 221 |
+
"integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
|
| 222 |
+
"license": "MIT",
|
| 223 |
+
"optional": true,
|
| 224 |
+
"dependencies": {
|
| 225 |
+
"bytes": "^3.1.2",
|
| 226 |
+
"content-type": "^1.0.5",
|
| 227 |
+
"debug": "^4.4.0",
|
| 228 |
+
"http-errors": "^2.0.0",
|
| 229 |
+
"iconv-lite": "^0.6.3",
|
| 230 |
+
"on-finished": "^2.4.1",
|
| 231 |
+
"qs": "^6.14.0",
|
| 232 |
+
"raw-body": "^3.0.0",
|
| 233 |
+
"type-is": "^2.0.0"
|
| 234 |
+
},
|
| 235 |
+
"engines": {
|
| 236 |
+
"node": ">=18"
|
| 237 |
+
}
|
| 238 |
+
},
|
| 239 |
+
"node_modules/bytes": {
|
| 240 |
+
"version": "3.1.2",
|
| 241 |
+
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
| 242 |
+
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
| 243 |
+
"license": "MIT",
|
| 244 |
+
"optional": true,
|
| 245 |
+
"engines": {
|
| 246 |
+
"node": ">= 0.8"
|
| 247 |
+
}
|
| 248 |
+
},
|
| 249 |
+
"node_modules/call-bind-apply-helpers": {
|
| 250 |
+
"version": "1.0.2",
|
| 251 |
+
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
| 252 |
+
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
| 253 |
+
"license": "MIT",
|
| 254 |
+
"optional": true,
|
| 255 |
+
"dependencies": {
|
| 256 |
+
"es-errors": "^1.3.0",
|
| 257 |
+
"function-bind": "^1.1.2"
|
| 258 |
+
},
|
| 259 |
+
"engines": {
|
| 260 |
+
"node": ">= 0.4"
|
| 261 |
+
}
|
| 262 |
+
},
|
| 263 |
+
"node_modules/call-bound": {
|
| 264 |
+
"version": "1.0.4",
|
| 265 |
+
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
|
| 266 |
+
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
|
| 267 |
+
"license": "MIT",
|
| 268 |
+
"optional": true,
|
| 269 |
+
"dependencies": {
|
| 270 |
+
"call-bind-apply-helpers": "^1.0.2",
|
| 271 |
+
"get-intrinsic": "^1.3.0"
|
| 272 |
+
},
|
| 273 |
+
"engines": {
|
| 274 |
+
"node": ">= 0.4"
|
| 275 |
+
},
|
| 276 |
+
"funding": {
|
| 277 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 278 |
+
}
|
| 279 |
+
},
|
| 280 |
+
"node_modules/cli-cursor": {
|
| 281 |
+
"version": "5.0.0",
|
| 282 |
+
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
|
| 283 |
+
"integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
|
| 284 |
+
"license": "MIT",
|
| 285 |
+
"dependencies": {
|
| 286 |
+
"restore-cursor": "^5.0.0"
|
| 287 |
+
},
|
| 288 |
+
"engines": {
|
| 289 |
+
"node": ">=18"
|
| 290 |
+
},
|
| 291 |
+
"funding": {
|
| 292 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 293 |
+
}
|
| 294 |
+
},
|
| 295 |
+
"node_modules/cli-truncate": {
|
| 296 |
+
"version": "4.0.0",
|
| 297 |
+
"resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz",
|
| 298 |
+
"integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==",
|
| 299 |
+
"license": "MIT",
|
| 300 |
+
"dependencies": {
|
| 301 |
+
"slice-ansi": "^5.0.0",
|
| 302 |
+
"string-width": "^7.0.0"
|
| 303 |
+
},
|
| 304 |
+
"engines": {
|
| 305 |
+
"node": ">=18"
|
| 306 |
+
},
|
| 307 |
+
"funding": {
|
| 308 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 309 |
+
}
|
| 310 |
+
},
|
| 311 |
+
"node_modules/colorette": {
|
| 312 |
+
"version": "2.0.20",
|
| 313 |
+
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
|
| 314 |
+
"integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
|
| 315 |
+
"license": "MIT"
|
| 316 |
+
},
|
| 317 |
+
"node_modules/content-disposition": {
|
| 318 |
+
"version": "1.0.0",
|
| 319 |
+
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
|
| 320 |
+
"integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==",
|
| 321 |
+
"license": "MIT",
|
| 322 |
+
"optional": true,
|
| 323 |
+
"dependencies": {
|
| 324 |
+
"safe-buffer": "5.2.1"
|
| 325 |
+
},
|
| 326 |
+
"engines": {
|
| 327 |
+
"node": ">= 0.6"
|
| 328 |
+
}
|
| 329 |
+
},
|
| 330 |
+
"node_modules/content-type": {
|
| 331 |
+
"version": "1.0.5",
|
| 332 |
+
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
| 333 |
+
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
| 334 |
+
"license": "MIT",
|
| 335 |
+
"optional": true,
|
| 336 |
+
"engines": {
|
| 337 |
+
"node": ">= 0.6"
|
| 338 |
+
}
|
| 339 |
+
},
|
| 340 |
+
"node_modules/cookie": {
|
| 341 |
+
"version": "0.7.2",
|
| 342 |
+
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
| 343 |
+
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
| 344 |
+
"license": "MIT",
|
| 345 |
+
"optional": true,
|
| 346 |
+
"engines": {
|
| 347 |
+
"node": ">= 0.6"
|
| 348 |
+
}
|
| 349 |
+
},
|
| 350 |
+
"node_modules/cookie-signature": {
|
| 351 |
+
"version": "1.2.2",
|
| 352 |
+
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
|
| 353 |
+
"integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
|
| 354 |
+
"license": "MIT",
|
| 355 |
+
"optional": true,
|
| 356 |
+
"engines": {
|
| 357 |
+
"node": ">=6.6.0"
|
| 358 |
+
}
|
| 359 |
+
},
|
| 360 |
+
"node_modules/cors": {
|
| 361 |
+
"version": "2.8.5",
|
| 362 |
+
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
| 363 |
+
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
|
| 364 |
+
"license": "MIT",
|
| 365 |
+
"optional": true,
|
| 366 |
+
"dependencies": {
|
| 367 |
+
"object-assign": "^4",
|
| 368 |
+
"vary": "^1"
|
| 369 |
+
},
|
| 370 |
+
"engines": {
|
| 371 |
+
"node": ">= 0.10"
|
| 372 |
+
}
|
| 373 |
+
},
|
| 374 |
+
"node_modules/cross-spawn": {
|
| 375 |
+
"version": "7.0.6",
|
| 376 |
+
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
| 377 |
+
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
| 378 |
+
"license": "MIT",
|
| 379 |
+
"optional": true,
|
| 380 |
+
"dependencies": {
|
| 381 |
+
"path-key": "^3.1.0",
|
| 382 |
+
"shebang-command": "^2.0.0",
|
| 383 |
+
"which": "^2.0.1"
|
| 384 |
+
},
|
| 385 |
+
"engines": {
|
| 386 |
+
"node": ">= 8"
|
| 387 |
+
}
|
| 388 |
+
},
|
| 389 |
+
"node_modules/debug": {
|
| 390 |
+
"version": "4.4.1",
|
| 391 |
+
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
| 392 |
+
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
|
| 393 |
+
"license": "MIT",
|
| 394 |
+
"dependencies": {
|
| 395 |
+
"ms": "^2.1.3"
|
| 396 |
+
},
|
| 397 |
+
"engines": {
|
| 398 |
+
"node": ">=6.0"
|
| 399 |
+
},
|
| 400 |
+
"peerDependenciesMeta": {
|
| 401 |
+
"supports-color": {
|
| 402 |
+
"optional": true
|
| 403 |
+
}
|
| 404 |
+
}
|
| 405 |
+
},
|
| 406 |
+
"node_modules/depd": {
|
| 407 |
+
"version": "2.0.0",
|
| 408 |
+
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
| 409 |
+
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
|
| 410 |
+
"license": "MIT",
|
| 411 |
+
"optional": true,
|
| 412 |
+
"engines": {
|
| 413 |
+
"node": ">= 0.8"
|
| 414 |
+
}
|
| 415 |
+
},
|
| 416 |
+
"node_modules/dunder-proto": {
|
| 417 |
+
"version": "1.0.1",
|
| 418 |
+
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
| 419 |
+
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
| 420 |
+
"license": "MIT",
|
| 421 |
+
"optional": true,
|
| 422 |
+
"dependencies": {
|
| 423 |
+
"call-bind-apply-helpers": "^1.0.1",
|
| 424 |
+
"es-errors": "^1.3.0",
|
| 425 |
+
"gopd": "^1.2.0"
|
| 426 |
+
},
|
| 427 |
+
"engines": {
|
| 428 |
+
"node": ">= 0.4"
|
| 429 |
+
}
|
| 430 |
+
},
|
| 431 |
+
"node_modules/ee-first": {
|
| 432 |
+
"version": "1.1.1",
|
| 433 |
+
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
| 434 |
+
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
|
| 435 |
+
"license": "MIT",
|
| 436 |
+
"optional": true
|
| 437 |
+
},
|
| 438 |
+
"node_modules/emoji-regex": {
|
| 439 |
+
"version": "10.4.0",
|
| 440 |
+
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
|
| 441 |
+
"integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==",
|
| 442 |
+
"license": "MIT"
|
| 443 |
+
},
|
| 444 |
+
"node_modules/encodeurl": {
|
| 445 |
+
"version": "2.0.0",
|
| 446 |
+
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
| 447 |
+
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
|
| 448 |
+
"license": "MIT",
|
| 449 |
+
"optional": true,
|
| 450 |
+
"engines": {
|
| 451 |
+
"node": ">= 0.8"
|
| 452 |
+
}
|
| 453 |
+
},
|
| 454 |
+
"node_modules/environment": {
|
| 455 |
+
"version": "1.1.0",
|
| 456 |
+
"resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
|
| 457 |
+
"integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
|
| 458 |
+
"license": "MIT",
|
| 459 |
+
"engines": {
|
| 460 |
+
"node": ">=18"
|
| 461 |
+
},
|
| 462 |
+
"funding": {
|
| 463 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 464 |
+
}
|
| 465 |
+
},
|
| 466 |
+
"node_modules/es-define-property": {
|
| 467 |
+
"version": "1.0.1",
|
| 468 |
+
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
| 469 |
+
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
| 470 |
+
"license": "MIT",
|
| 471 |
+
"optional": true,
|
| 472 |
+
"engines": {
|
| 473 |
+
"node": ">= 0.4"
|
| 474 |
+
}
|
| 475 |
+
},
|
| 476 |
+
"node_modules/es-errors": {
|
| 477 |
+
"version": "1.3.0",
|
| 478 |
+
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
| 479 |
+
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
| 480 |
+
"license": "MIT",
|
| 481 |
+
"optional": true,
|
| 482 |
+
"engines": {
|
| 483 |
+
"node": ">= 0.4"
|
| 484 |
+
}
|
| 485 |
+
},
|
| 486 |
+
"node_modules/es-object-atoms": {
|
| 487 |
+
"version": "1.1.1",
|
| 488 |
+
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
| 489 |
+
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
| 490 |
+
"license": "MIT",
|
| 491 |
+
"optional": true,
|
| 492 |
+
"dependencies": {
|
| 493 |
+
"es-errors": "^1.3.0"
|
| 494 |
+
},
|
| 495 |
+
"engines": {
|
| 496 |
+
"node": ">= 0.4"
|
| 497 |
+
}
|
| 498 |
+
},
|
| 499 |
+
"node_modules/escape-html": {
|
| 500 |
+
"version": "1.0.3",
|
| 501 |
+
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
| 502 |
+
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
|
| 503 |
+
"license": "MIT",
|
| 504 |
+
"optional": true
|
| 505 |
+
},
|
| 506 |
+
"node_modules/etag": {
|
| 507 |
+
"version": "1.8.1",
|
| 508 |
+
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
| 509 |
+
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
|
| 510 |
+
"license": "MIT",
|
| 511 |
+
"optional": true,
|
| 512 |
+
"engines": {
|
| 513 |
+
"node": ">= 0.6"
|
| 514 |
+
}
|
| 515 |
+
},
|
| 516 |
+
"node_modules/eventemitter3": {
|
| 517 |
+
"version": "5.0.1",
|
| 518 |
+
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
|
| 519 |
+
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
|
| 520 |
+
"license": "MIT"
|
| 521 |
+
},
|
| 522 |
+
"node_modules/eventsource": {
|
| 523 |
+
"version": "3.0.7",
|
| 524 |
+
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz",
|
| 525 |
+
"integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==",
|
| 526 |
+
"license": "MIT",
|
| 527 |
+
"optional": true,
|
| 528 |
+
"dependencies": {
|
| 529 |
+
"eventsource-parser": "^3.0.1"
|
| 530 |
+
},
|
| 531 |
+
"engines": {
|
| 532 |
+
"node": ">=18.0.0"
|
| 533 |
+
}
|
| 534 |
+
},
|
| 535 |
+
"node_modules/eventsource-parser": {
|
| 536 |
+
"version": "3.0.3",
|
| 537 |
+
"resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.3.tgz",
|
| 538 |
+
"integrity": "sha512-nVpZkTMM9rF6AQ9gPJpFsNAMt48wIzB5TQgiTLdHiuO8XEDhUgZEhqKlZWXbIzo9VmJ/HvysHqEaVeD5v9TPvA==",
|
| 539 |
+
"license": "MIT",
|
| 540 |
+
"optional": true,
|
| 541 |
+
"engines": {
|
| 542 |
+
"node": ">=20.0.0"
|
| 543 |
+
}
|
| 544 |
+
},
|
| 545 |
+
"node_modules/express": {
|
| 546 |
+
"version": "5.1.0",
|
| 547 |
+
"resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
|
| 548 |
+
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
|
| 549 |
+
"license": "MIT",
|
| 550 |
+
"optional": true,
|
| 551 |
+
"dependencies": {
|
| 552 |
+
"accepts": "^2.0.0",
|
| 553 |
+
"body-parser": "^2.2.0",
|
| 554 |
+
"content-disposition": "^1.0.0",
|
| 555 |
+
"content-type": "^1.0.5",
|
| 556 |
+
"cookie": "^0.7.1",
|
| 557 |
+
"cookie-signature": "^1.2.1",
|
| 558 |
+
"debug": "^4.4.0",
|
| 559 |
+
"encodeurl": "^2.0.0",
|
| 560 |
+
"escape-html": "^1.0.3",
|
| 561 |
+
"etag": "^1.8.1",
|
| 562 |
+
"finalhandler": "^2.1.0",
|
| 563 |
+
"fresh": "^2.0.0",
|
| 564 |
+
"http-errors": "^2.0.0",
|
| 565 |
+
"merge-descriptors": "^2.0.0",
|
| 566 |
+
"mime-types": "^3.0.0",
|
| 567 |
+
"on-finished": "^2.4.1",
|
| 568 |
+
"once": "^1.4.0",
|
| 569 |
+
"parseurl": "^1.3.3",
|
| 570 |
+
"proxy-addr": "^2.0.7",
|
| 571 |
+
"qs": "^6.14.0",
|
| 572 |
+
"range-parser": "^1.2.1",
|
| 573 |
+
"router": "^2.2.0",
|
| 574 |
+
"send": "^1.1.0",
|
| 575 |
+
"serve-static": "^2.2.0",
|
| 576 |
+
"statuses": "^2.0.1",
|
| 577 |
+
"type-is": "^2.0.1",
|
| 578 |
+
"vary": "^1.1.2"
|
| 579 |
+
},
|
| 580 |
+
"engines": {
|
| 581 |
+
"node": ">= 18"
|
| 582 |
+
},
|
| 583 |
+
"funding": {
|
| 584 |
+
"type": "opencollective",
|
| 585 |
+
"url": "https://opencollective.com/express"
|
| 586 |
+
}
|
| 587 |
+
},
|
| 588 |
+
"node_modules/express-rate-limit": {
|
| 589 |
+
"version": "7.5.1",
|
| 590 |
+
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz",
|
| 591 |
+
"integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==",
|
| 592 |
+
"license": "MIT",
|
| 593 |
+
"optional": true,
|
| 594 |
+
"engines": {
|
| 595 |
+
"node": ">= 16"
|
| 596 |
+
},
|
| 597 |
+
"funding": {
|
| 598 |
+
"url": "https://github.com/sponsors/express-rate-limit"
|
| 599 |
+
},
|
| 600 |
+
"peerDependencies": {
|
| 601 |
+
"express": ">= 4.11"
|
| 602 |
+
}
|
| 603 |
+
},
|
| 604 |
+
"node_modules/fast-deep-equal": {
|
| 605 |
+
"version": "3.1.3",
|
| 606 |
+
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
| 607 |
+
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
| 608 |
+
"license": "MIT"
|
| 609 |
+
},
|
| 610 |
+
"node_modules/fast-json-stable-stringify": {
|
| 611 |
+
"version": "2.1.0",
|
| 612 |
+
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
| 613 |
+
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
|
| 614 |
+
"license": "MIT",
|
| 615 |
+
"optional": true
|
| 616 |
+
},
|
| 617 |
+
"node_modules/fast-uri": {
|
| 618 |
+
"version": "3.0.6",
|
| 619 |
+
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz",
|
| 620 |
+
"integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==",
|
| 621 |
+
"funding": [
|
| 622 |
+
{
|
| 623 |
+
"type": "github",
|
| 624 |
+
"url": "https://github.com/sponsors/fastify"
|
| 625 |
+
},
|
| 626 |
+
{
|
| 627 |
+
"type": "opencollective",
|
| 628 |
+
"url": "https://opencollective.com/fastify"
|
| 629 |
+
}
|
| 630 |
+
],
|
| 631 |
+
"license": "BSD-3-Clause"
|
| 632 |
+
},
|
| 633 |
+
"node_modules/finalhandler": {
|
| 634 |
+
"version": "2.1.0",
|
| 635 |
+
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
|
| 636 |
+
"integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==",
|
| 637 |
+
"license": "MIT",
|
| 638 |
+
"optional": true,
|
| 639 |
+
"dependencies": {
|
| 640 |
+
"debug": "^4.4.0",
|
| 641 |
+
"encodeurl": "^2.0.0",
|
| 642 |
+
"escape-html": "^1.0.3",
|
| 643 |
+
"on-finished": "^2.4.1",
|
| 644 |
+
"parseurl": "^1.3.3",
|
| 645 |
+
"statuses": "^2.0.1"
|
| 646 |
+
},
|
| 647 |
+
"engines": {
|
| 648 |
+
"node": ">= 0.8"
|
| 649 |
+
}
|
| 650 |
+
},
|
| 651 |
+
"node_modules/forwarded": {
|
| 652 |
+
"version": "0.2.0",
|
| 653 |
+
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
| 654 |
+
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
|
| 655 |
+
"license": "MIT",
|
| 656 |
+
"optional": true,
|
| 657 |
+
"engines": {
|
| 658 |
+
"node": ">= 0.6"
|
| 659 |
+
}
|
| 660 |
+
},
|
| 661 |
+
"node_modules/fresh": {
|
| 662 |
+
"version": "2.0.0",
|
| 663 |
+
"resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
|
| 664 |
+
"integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
|
| 665 |
+
"license": "MIT",
|
| 666 |
+
"optional": true,
|
| 667 |
+
"engines": {
|
| 668 |
+
"node": ">= 0.8"
|
| 669 |
+
}
|
| 670 |
+
},
|
| 671 |
+
"node_modules/function-bind": {
|
| 672 |
+
"version": "1.1.2",
|
| 673 |
+
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
| 674 |
+
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
| 675 |
+
"license": "MIT",
|
| 676 |
+
"optional": true,
|
| 677 |
+
"funding": {
|
| 678 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 679 |
+
}
|
| 680 |
+
},
|
| 681 |
+
"node_modules/get-east-asian-width": {
|
| 682 |
+
"version": "1.3.0",
|
| 683 |
+
"resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz",
|
| 684 |
+
"integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==",
|
| 685 |
+
"license": "MIT",
|
| 686 |
+
"engines": {
|
| 687 |
+
"node": ">=18"
|
| 688 |
+
},
|
| 689 |
+
"funding": {
|
| 690 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 691 |
+
}
|
| 692 |
+
},
|
| 693 |
+
"node_modules/get-intrinsic": {
|
| 694 |
+
"version": "1.3.0",
|
| 695 |
+
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
| 696 |
+
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
| 697 |
+
"license": "MIT",
|
| 698 |
+
"optional": true,
|
| 699 |
+
"dependencies": {
|
| 700 |
+
"call-bind-apply-helpers": "^1.0.2",
|
| 701 |
+
"es-define-property": "^1.0.1",
|
| 702 |
+
"es-errors": "^1.3.0",
|
| 703 |
+
"es-object-atoms": "^1.1.1",
|
| 704 |
+
"function-bind": "^1.1.2",
|
| 705 |
+
"get-proto": "^1.0.1",
|
| 706 |
+
"gopd": "^1.2.0",
|
| 707 |
+
"has-symbols": "^1.1.0",
|
| 708 |
+
"hasown": "^2.0.2",
|
| 709 |
+
"math-intrinsics": "^1.1.0"
|
| 710 |
+
},
|
| 711 |
+
"engines": {
|
| 712 |
+
"node": ">= 0.4"
|
| 713 |
+
},
|
| 714 |
+
"funding": {
|
| 715 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 716 |
+
}
|
| 717 |
+
},
|
| 718 |
+
"node_modules/get-proto": {
|
| 719 |
+
"version": "1.0.1",
|
| 720 |
+
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
| 721 |
+
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
| 722 |
+
"license": "MIT",
|
| 723 |
+
"optional": true,
|
| 724 |
+
"dependencies": {
|
| 725 |
+
"dunder-proto": "^1.0.1",
|
| 726 |
+
"es-object-atoms": "^1.0.0"
|
| 727 |
+
},
|
| 728 |
+
"engines": {
|
| 729 |
+
"node": ">= 0.4"
|
| 730 |
+
}
|
| 731 |
+
},
|
| 732 |
+
"node_modules/gopd": {
|
| 733 |
+
"version": "1.2.0",
|
| 734 |
+
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
| 735 |
+
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
| 736 |
+
"license": "MIT",
|
| 737 |
+
"optional": true,
|
| 738 |
+
"engines": {
|
| 739 |
+
"node": ">= 0.4"
|
| 740 |
+
},
|
| 741 |
+
"funding": {
|
| 742 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 743 |
+
}
|
| 744 |
+
},
|
| 745 |
+
"node_modules/has-symbols": {
|
| 746 |
+
"version": "1.1.0",
|
| 747 |
+
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
| 748 |
+
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
| 749 |
+
"license": "MIT",
|
| 750 |
+
"optional": true,
|
| 751 |
+
"engines": {
|
| 752 |
+
"node": ">= 0.4"
|
| 753 |
+
},
|
| 754 |
+
"funding": {
|
| 755 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 756 |
+
}
|
| 757 |
+
},
|
| 758 |
+
"node_modules/hasown": {
|
| 759 |
+
"version": "2.0.2",
|
| 760 |
+
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
| 761 |
+
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
| 762 |
+
"license": "MIT",
|
| 763 |
+
"optional": true,
|
| 764 |
+
"dependencies": {
|
| 765 |
+
"function-bind": "^1.1.2"
|
| 766 |
+
},
|
| 767 |
+
"engines": {
|
| 768 |
+
"node": ">= 0.4"
|
| 769 |
+
}
|
| 770 |
+
},
|
| 771 |
+
"node_modules/http-errors": {
|
| 772 |
+
"version": "2.0.0",
|
| 773 |
+
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
| 774 |
+
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
|
| 775 |
+
"license": "MIT",
|
| 776 |
+
"optional": true,
|
| 777 |
+
"dependencies": {
|
| 778 |
+
"depd": "2.0.0",
|
| 779 |
+
"inherits": "2.0.4",
|
| 780 |
+
"setprototypeof": "1.2.0",
|
| 781 |
+
"statuses": "2.0.1",
|
| 782 |
+
"toidentifier": "1.0.1"
|
| 783 |
+
},
|
| 784 |
+
"engines": {
|
| 785 |
+
"node": ">= 0.8"
|
| 786 |
+
}
|
| 787 |
+
},
|
| 788 |
+
"node_modules/http-errors/node_modules/statuses": {
|
| 789 |
+
"version": "2.0.1",
|
| 790 |
+
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
| 791 |
+
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
|
| 792 |
+
"license": "MIT",
|
| 793 |
+
"optional": true,
|
| 794 |
+
"engines": {
|
| 795 |
+
"node": ">= 0.8"
|
| 796 |
+
}
|
| 797 |
+
},
|
| 798 |
+
"node_modules/iconv-lite": {
|
| 799 |
+
"version": "0.6.3",
|
| 800 |
+
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
| 801 |
+
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
| 802 |
+
"license": "MIT",
|
| 803 |
+
"optional": true,
|
| 804 |
+
"dependencies": {
|
| 805 |
+
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
| 806 |
+
},
|
| 807 |
+
"engines": {
|
| 808 |
+
"node": ">=0.10.0"
|
| 809 |
+
}
|
| 810 |
+
},
|
| 811 |
+
"node_modules/inherits": {
|
| 812 |
+
"version": "2.0.4",
|
| 813 |
+
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
| 814 |
+
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
| 815 |
+
"license": "ISC",
|
| 816 |
+
"optional": true
|
| 817 |
+
},
|
| 818 |
+
"node_modules/ipaddr.js": {
|
| 819 |
+
"version": "1.9.1",
|
| 820 |
+
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
| 821 |
+
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
|
| 822 |
+
"license": "MIT",
|
| 823 |
+
"optional": true,
|
| 824 |
+
"engines": {
|
| 825 |
+
"node": ">= 0.10"
|
| 826 |
+
}
|
| 827 |
+
},
|
| 828 |
+
"node_modules/is-fullwidth-code-point": {
|
| 829 |
+
"version": "4.0.0",
|
| 830 |
+
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz",
|
| 831 |
+
"integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==",
|
| 832 |
+
"license": "MIT",
|
| 833 |
+
"engines": {
|
| 834 |
+
"node": ">=12"
|
| 835 |
+
},
|
| 836 |
+
"funding": {
|
| 837 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 838 |
+
}
|
| 839 |
+
},
|
| 840 |
+
"node_modules/is-promise": {
|
| 841 |
+
"version": "4.0.0",
|
| 842 |
+
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
|
| 843 |
+
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
|
| 844 |
+
"license": "MIT",
|
| 845 |
+
"optional": true
|
| 846 |
+
},
|
| 847 |
+
"node_modules/isexe": {
|
| 848 |
+
"version": "2.0.0",
|
| 849 |
+
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
| 850 |
+
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
| 851 |
+
"license": "ISC",
|
| 852 |
+
"optional": true
|
| 853 |
+
},
|
| 854 |
+
"node_modules/json-schema-traverse": {
|
| 855 |
+
"version": "1.0.0",
|
| 856 |
+
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
| 857 |
+
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
|
| 858 |
+
"license": "MIT"
|
| 859 |
+
},
|
| 860 |
+
"node_modules/listr2": {
|
| 861 |
+
"version": "9.0.1",
|
| 862 |
+
"resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.1.tgz",
|
| 863 |
+
"integrity": "sha512-SL0JY3DaxylDuo/MecFeiC+7pedM0zia33zl0vcjgwcq1q1FWWF1To9EIauPbl8GbMCU0R2e0uJ8bZunhYKD2g==",
|
| 864 |
+
"license": "MIT",
|
| 865 |
+
"dependencies": {
|
| 866 |
+
"cli-truncate": "^4.0.0",
|
| 867 |
+
"colorette": "^2.0.20",
|
| 868 |
+
"eventemitter3": "^5.0.1",
|
| 869 |
+
"log-update": "^6.1.0",
|
| 870 |
+
"rfdc": "^1.4.1",
|
| 871 |
+
"wrap-ansi": "^9.0.0"
|
| 872 |
+
},
|
| 873 |
+
"engines": {
|
| 874 |
+
"node": ">=20.0.0"
|
| 875 |
+
}
|
| 876 |
+
},
|
| 877 |
+
"node_modules/log-update": {
|
| 878 |
+
"version": "6.1.0",
|
| 879 |
+
"resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz",
|
| 880 |
+
"integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==",
|
| 881 |
+
"license": "MIT",
|
| 882 |
+
"dependencies": {
|
| 883 |
+
"ansi-escapes": "^7.0.0",
|
| 884 |
+
"cli-cursor": "^5.0.0",
|
| 885 |
+
"slice-ansi": "^7.1.0",
|
| 886 |
+
"strip-ansi": "^7.1.0",
|
| 887 |
+
"wrap-ansi": "^9.0.0"
|
| 888 |
+
},
|
| 889 |
+
"engines": {
|
| 890 |
+
"node": ">=18"
|
| 891 |
+
},
|
| 892 |
+
"funding": {
|
| 893 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 894 |
+
}
|
| 895 |
+
},
|
| 896 |
+
"node_modules/log-update/node_modules/is-fullwidth-code-point": {
|
| 897 |
+
"version": "5.0.0",
|
| 898 |
+
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz",
|
| 899 |
+
"integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==",
|
| 900 |
+
"license": "MIT",
|
| 901 |
+
"dependencies": {
|
| 902 |
+
"get-east-asian-width": "^1.0.0"
|
| 903 |
+
},
|
| 904 |
+
"engines": {
|
| 905 |
+
"node": ">=18"
|
| 906 |
+
},
|
| 907 |
+
"funding": {
|
| 908 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 909 |
+
}
|
| 910 |
+
},
|
| 911 |
+
"node_modules/log-update/node_modules/slice-ansi": {
|
| 912 |
+
"version": "7.1.0",
|
| 913 |
+
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz",
|
| 914 |
+
"integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==",
|
| 915 |
+
"license": "MIT",
|
| 916 |
+
"dependencies": {
|
| 917 |
+
"ansi-styles": "^6.2.1",
|
| 918 |
+
"is-fullwidth-code-point": "^5.0.0"
|
| 919 |
+
},
|
| 920 |
+
"engines": {
|
| 921 |
+
"node": ">=18"
|
| 922 |
+
},
|
| 923 |
+
"funding": {
|
| 924 |
+
"url": "https://github.com/chalk/slice-ansi?sponsor=1"
|
| 925 |
+
}
|
| 926 |
+
},
|
| 927 |
+
"node_modules/math-intrinsics": {
|
| 928 |
+
"version": "1.1.0",
|
| 929 |
+
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
| 930 |
+
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
| 931 |
+
"license": "MIT",
|
| 932 |
+
"optional": true,
|
| 933 |
+
"engines": {
|
| 934 |
+
"node": ">= 0.4"
|
| 935 |
+
}
|
| 936 |
+
},
|
| 937 |
+
"node_modules/media-typer": {
|
| 938 |
+
"version": "1.1.0",
|
| 939 |
+
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
|
| 940 |
+
"integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
|
| 941 |
+
"license": "MIT",
|
| 942 |
+
"optional": true,
|
| 943 |
+
"engines": {
|
| 944 |
+
"node": ">= 0.8"
|
| 945 |
+
}
|
| 946 |
+
},
|
| 947 |
+
"node_modules/merge-descriptors": {
|
| 948 |
+
"version": "2.0.0",
|
| 949 |
+
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
|
| 950 |
+
"integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
|
| 951 |
+
"license": "MIT",
|
| 952 |
+
"optional": true,
|
| 953 |
+
"engines": {
|
| 954 |
+
"node": ">=18"
|
| 955 |
+
},
|
| 956 |
+
"funding": {
|
| 957 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 958 |
+
}
|
| 959 |
+
},
|
| 960 |
+
"node_modules/mime-db": {
|
| 961 |
+
"version": "1.54.0",
|
| 962 |
+
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
| 963 |
+
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
|
| 964 |
+
"license": "MIT",
|
| 965 |
+
"optional": true,
|
| 966 |
+
"engines": {
|
| 967 |
+
"node": ">= 0.6"
|
| 968 |
+
}
|
| 969 |
+
},
|
| 970 |
+
"node_modules/mime-types": {
|
| 971 |
+
"version": "3.0.1",
|
| 972 |
+
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
|
| 973 |
+
"integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
|
| 974 |
+
"license": "MIT",
|
| 975 |
+
"optional": true,
|
| 976 |
+
"dependencies": {
|
| 977 |
+
"mime-db": "^1.54.0"
|
| 978 |
+
},
|
| 979 |
+
"engines": {
|
| 980 |
+
"node": ">= 0.6"
|
| 981 |
+
}
|
| 982 |
+
},
|
| 983 |
+
"node_modules/mimic-function": {
|
| 984 |
+
"version": "5.0.1",
|
| 985 |
+
"resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
|
| 986 |
+
"integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
|
| 987 |
+
"license": "MIT",
|
| 988 |
+
"engines": {
|
| 989 |
+
"node": ">=18"
|
| 990 |
+
},
|
| 991 |
+
"funding": {
|
| 992 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 993 |
+
}
|
| 994 |
+
},
|
| 995 |
+
"node_modules/ms": {
|
| 996 |
+
"version": "2.1.3",
|
| 997 |
+
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
| 998 |
+
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
| 999 |
+
"license": "MIT"
|
| 1000 |
+
},
|
| 1001 |
+
"node_modules/negotiator": {
|
| 1002 |
+
"version": "1.0.0",
|
| 1003 |
+
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
|
| 1004 |
+
"integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
|
| 1005 |
+
"license": "MIT",
|
| 1006 |
+
"optional": true,
|
| 1007 |
+
"engines": {
|
| 1008 |
+
"node": ">= 0.6"
|
| 1009 |
+
}
|
| 1010 |
+
},
|
| 1011 |
+
"node_modules/object-assign": {
|
| 1012 |
+
"version": "4.1.1",
|
| 1013 |
+
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
| 1014 |
+
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
| 1015 |
+
"license": "MIT",
|
| 1016 |
+
"optional": true,
|
| 1017 |
+
"engines": {
|
| 1018 |
+
"node": ">=0.10.0"
|
| 1019 |
+
}
|
| 1020 |
+
},
|
| 1021 |
+
"node_modules/object-inspect": {
|
| 1022 |
+
"version": "1.13.4",
|
| 1023 |
+
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
| 1024 |
+
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
|
| 1025 |
+
"license": "MIT",
|
| 1026 |
+
"optional": true,
|
| 1027 |
+
"engines": {
|
| 1028 |
+
"node": ">= 0.4"
|
| 1029 |
+
},
|
| 1030 |
+
"funding": {
|
| 1031 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 1032 |
+
}
|
| 1033 |
+
},
|
| 1034 |
+
"node_modules/on-finished": {
|
| 1035 |
+
"version": "2.4.1",
|
| 1036 |
+
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
| 1037 |
+
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
| 1038 |
+
"license": "MIT",
|
| 1039 |
+
"optional": true,
|
| 1040 |
+
"dependencies": {
|
| 1041 |
+
"ee-first": "1.1.1"
|
| 1042 |
+
},
|
| 1043 |
+
"engines": {
|
| 1044 |
+
"node": ">= 0.8"
|
| 1045 |
+
}
|
| 1046 |
+
},
|
| 1047 |
+
"node_modules/once": {
|
| 1048 |
+
"version": "1.4.0",
|
| 1049 |
+
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
| 1050 |
+
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
| 1051 |
+
"license": "ISC",
|
| 1052 |
+
"optional": true,
|
| 1053 |
+
"dependencies": {
|
| 1054 |
+
"wrappy": "1"
|
| 1055 |
+
}
|
| 1056 |
+
},
|
| 1057 |
+
"node_modules/onetime": {
|
| 1058 |
+
"version": "7.0.0",
|
| 1059 |
+
"resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
|
| 1060 |
+
"integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
|
| 1061 |
+
"license": "MIT",
|
| 1062 |
+
"dependencies": {
|
| 1063 |
+
"mimic-function": "^5.0.0"
|
| 1064 |
+
},
|
| 1065 |
+
"engines": {
|
| 1066 |
+
"node": ">=18"
|
| 1067 |
+
},
|
| 1068 |
+
"funding": {
|
| 1069 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 1070 |
+
}
|
| 1071 |
+
},
|
| 1072 |
+
"node_modules/openai": {
|
| 1073 |
+
"version": "5.12.0",
|
| 1074 |
+
"resolved": "https://registry.npmjs.org/openai/-/openai-5.12.0.tgz",
|
| 1075 |
+
"integrity": "sha512-vUdt02xiWgOHiYUmW0Hj1Qu9OKAiVQu5Bd547ktVCiMKC1BkB5L3ImeEnCyq3WpRKR6ZTaPgekzqdozwdPs7Lg==",
|
| 1076 |
+
"license": "Apache-2.0",
|
| 1077 |
+
"bin": {
|
| 1078 |
+
"openai": "bin/cli"
|
| 1079 |
+
},
|
| 1080 |
+
"peerDependencies": {
|
| 1081 |
+
"ws": "^8.18.0",
|
| 1082 |
+
"zod": "^3.23.8"
|
| 1083 |
+
},
|
| 1084 |
+
"peerDependenciesMeta": {
|
| 1085 |
+
"ws": {
|
| 1086 |
+
"optional": true
|
| 1087 |
+
},
|
| 1088 |
+
"zod": {
|
| 1089 |
+
"optional": true
|
| 1090 |
+
}
|
| 1091 |
+
}
|
| 1092 |
+
},
|
| 1093 |
+
"node_modules/parseurl": {
|
| 1094 |
+
"version": "1.3.3",
|
| 1095 |
+
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
| 1096 |
+
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
|
| 1097 |
+
"license": "MIT",
|
| 1098 |
+
"optional": true,
|
| 1099 |
+
"engines": {
|
| 1100 |
+
"node": ">= 0.8"
|
| 1101 |
+
}
|
| 1102 |
+
},
|
| 1103 |
+
"node_modules/path-key": {
|
| 1104 |
+
"version": "3.1.1",
|
| 1105 |
+
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
| 1106 |
+
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
| 1107 |
+
"license": "MIT",
|
| 1108 |
+
"optional": true,
|
| 1109 |
+
"engines": {
|
| 1110 |
+
"node": ">=8"
|
| 1111 |
+
}
|
| 1112 |
+
},
|
| 1113 |
+
"node_modules/path-to-regexp": {
|
| 1114 |
+
"version": "8.2.0",
|
| 1115 |
+
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
|
| 1116 |
+
"integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==",
|
| 1117 |
+
"license": "MIT",
|
| 1118 |
+
"optional": true,
|
| 1119 |
+
"engines": {
|
| 1120 |
+
"node": ">=16"
|
| 1121 |
+
}
|
| 1122 |
+
},
|
| 1123 |
+
"node_modules/pkce-challenge": {
|
| 1124 |
+
"version": "5.0.0",
|
| 1125 |
+
"resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz",
|
| 1126 |
+
"integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==",
|
| 1127 |
+
"license": "MIT",
|
| 1128 |
+
"optional": true,
|
| 1129 |
+
"engines": {
|
| 1130 |
+
"node": ">=16.20.0"
|
| 1131 |
+
}
|
| 1132 |
+
},
|
| 1133 |
+
"node_modules/proxy-addr": {
|
| 1134 |
+
"version": "2.0.7",
|
| 1135 |
+
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
| 1136 |
+
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
|
| 1137 |
+
"license": "MIT",
|
| 1138 |
+
"optional": true,
|
| 1139 |
+
"dependencies": {
|
| 1140 |
+
"forwarded": "0.2.0",
|
| 1141 |
+
"ipaddr.js": "1.9.1"
|
| 1142 |
+
},
|
| 1143 |
+
"engines": {
|
| 1144 |
+
"node": ">= 0.10"
|
| 1145 |
+
}
|
| 1146 |
+
},
|
| 1147 |
+
"node_modules/punycode": {
|
| 1148 |
+
"version": "2.3.1",
|
| 1149 |
+
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
| 1150 |
+
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
| 1151 |
+
"license": "MIT",
|
| 1152 |
+
"optional": true,
|
| 1153 |
+
"engines": {
|
| 1154 |
+
"node": ">=6"
|
| 1155 |
+
}
|
| 1156 |
+
},
|
| 1157 |
+
"node_modules/qs": {
|
| 1158 |
+
"version": "6.14.0",
|
| 1159 |
+
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
|
| 1160 |
+
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
|
| 1161 |
+
"license": "BSD-3-Clause",
|
| 1162 |
+
"optional": true,
|
| 1163 |
+
"dependencies": {
|
| 1164 |
+
"side-channel": "^1.1.0"
|
| 1165 |
+
},
|
| 1166 |
+
"engines": {
|
| 1167 |
+
"node": ">=0.6"
|
| 1168 |
+
},
|
| 1169 |
+
"funding": {
|
| 1170 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 1171 |
+
}
|
| 1172 |
+
},
|
| 1173 |
+
"node_modules/range-parser": {
|
| 1174 |
+
"version": "1.2.1",
|
| 1175 |
+
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
| 1176 |
+
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
|
| 1177 |
+
"license": "MIT",
|
| 1178 |
+
"optional": true,
|
| 1179 |
+
"engines": {
|
| 1180 |
+
"node": ">= 0.6"
|
| 1181 |
+
}
|
| 1182 |
+
},
|
| 1183 |
+
"node_modules/raw-body": {
|
| 1184 |
+
"version": "3.0.0",
|
| 1185 |
+
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
|
| 1186 |
+
"integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
|
| 1187 |
+
"license": "MIT",
|
| 1188 |
+
"optional": true,
|
| 1189 |
+
"dependencies": {
|
| 1190 |
+
"bytes": "3.1.2",
|
| 1191 |
+
"http-errors": "2.0.0",
|
| 1192 |
+
"iconv-lite": "0.6.3",
|
| 1193 |
+
"unpipe": "1.0.0"
|
| 1194 |
+
},
|
| 1195 |
+
"engines": {
|
| 1196 |
+
"node": ">= 0.8"
|
| 1197 |
+
}
|
| 1198 |
+
},
|
| 1199 |
+
"node_modules/require-from-string": {
|
| 1200 |
+
"version": "2.0.2",
|
| 1201 |
+
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
| 1202 |
+
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
|
| 1203 |
+
"license": "MIT",
|
| 1204 |
+
"engines": {
|
| 1205 |
+
"node": ">=0.10.0"
|
| 1206 |
+
}
|
| 1207 |
+
},
|
| 1208 |
+
"node_modules/restore-cursor": {
|
| 1209 |
+
"version": "5.1.0",
|
| 1210 |
+
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
|
| 1211 |
+
"integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
|
| 1212 |
+
"license": "MIT",
|
| 1213 |
+
"dependencies": {
|
| 1214 |
+
"onetime": "^7.0.0",
|
| 1215 |
+
"signal-exit": "^4.1.0"
|
| 1216 |
+
},
|
| 1217 |
+
"engines": {
|
| 1218 |
+
"node": ">=18"
|
| 1219 |
+
},
|
| 1220 |
+
"funding": {
|
| 1221 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 1222 |
+
}
|
| 1223 |
+
},
|
| 1224 |
+
"node_modules/rfdc": {
|
| 1225 |
+
"version": "1.4.1",
|
| 1226 |
+
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
|
| 1227 |
+
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
|
| 1228 |
+
"license": "MIT"
|
| 1229 |
+
},
|
| 1230 |
+
"node_modules/router": {
|
| 1231 |
+
"version": "2.2.0",
|
| 1232 |
+
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
|
| 1233 |
+
"integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
|
| 1234 |
+
"license": "MIT",
|
| 1235 |
+
"optional": true,
|
| 1236 |
+
"dependencies": {
|
| 1237 |
+
"debug": "^4.4.0",
|
| 1238 |
+
"depd": "^2.0.0",
|
| 1239 |
+
"is-promise": "^4.0.0",
|
| 1240 |
+
"parseurl": "^1.3.3",
|
| 1241 |
+
"path-to-regexp": "^8.0.0"
|
| 1242 |
+
},
|
| 1243 |
+
"engines": {
|
| 1244 |
+
"node": ">= 18"
|
| 1245 |
+
}
|
| 1246 |
+
},
|
| 1247 |
+
"node_modules/safe-buffer": {
|
| 1248 |
+
"version": "5.2.1",
|
| 1249 |
+
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
| 1250 |
+
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
| 1251 |
+
"funding": [
|
| 1252 |
+
{
|
| 1253 |
+
"type": "github",
|
| 1254 |
+
"url": "https://github.com/sponsors/feross"
|
| 1255 |
+
},
|
| 1256 |
+
{
|
| 1257 |
+
"type": "patreon",
|
| 1258 |
+
"url": "https://www.patreon.com/feross"
|
| 1259 |
+
},
|
| 1260 |
+
{
|
| 1261 |
+
"type": "consulting",
|
| 1262 |
+
"url": "https://feross.org/support"
|
| 1263 |
+
}
|
| 1264 |
+
],
|
| 1265 |
+
"license": "MIT",
|
| 1266 |
+
"optional": true
|
| 1267 |
+
},
|
| 1268 |
+
"node_modules/safer-buffer": {
|
| 1269 |
+
"version": "2.1.2",
|
| 1270 |
+
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
| 1271 |
+
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
| 1272 |
+
"license": "MIT",
|
| 1273 |
+
"optional": true
|
| 1274 |
+
},
|
| 1275 |
+
"node_modules/send": {
|
| 1276 |
+
"version": "1.2.0",
|
| 1277 |
+
"resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
|
| 1278 |
+
"integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
|
| 1279 |
+
"license": "MIT",
|
| 1280 |
+
"optional": true,
|
| 1281 |
+
"dependencies": {
|
| 1282 |
+
"debug": "^4.3.5",
|
| 1283 |
+
"encodeurl": "^2.0.0",
|
| 1284 |
+
"escape-html": "^1.0.3",
|
| 1285 |
+
"etag": "^1.8.1",
|
| 1286 |
+
"fresh": "^2.0.0",
|
| 1287 |
+
"http-errors": "^2.0.0",
|
| 1288 |
+
"mime-types": "^3.0.1",
|
| 1289 |
+
"ms": "^2.1.3",
|
| 1290 |
+
"on-finished": "^2.4.1",
|
| 1291 |
+
"range-parser": "^1.2.1",
|
| 1292 |
+
"statuses": "^2.0.1"
|
| 1293 |
+
},
|
| 1294 |
+
"engines": {
|
| 1295 |
+
"node": ">= 18"
|
| 1296 |
+
}
|
| 1297 |
+
},
|
| 1298 |
+
"node_modules/serve-static": {
|
| 1299 |
+
"version": "2.2.0",
|
| 1300 |
+
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz",
|
| 1301 |
+
"integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
|
| 1302 |
+
"license": "MIT",
|
| 1303 |
+
"optional": true,
|
| 1304 |
+
"dependencies": {
|
| 1305 |
+
"encodeurl": "^2.0.0",
|
| 1306 |
+
"escape-html": "^1.0.3",
|
| 1307 |
+
"parseurl": "^1.3.3",
|
| 1308 |
+
"send": "^1.2.0"
|
| 1309 |
+
},
|
| 1310 |
+
"engines": {
|
| 1311 |
+
"node": ">= 18"
|
| 1312 |
+
}
|
| 1313 |
+
},
|
| 1314 |
+
"node_modules/setprototypeof": {
|
| 1315 |
+
"version": "1.2.0",
|
| 1316 |
+
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
| 1317 |
+
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
|
| 1318 |
+
"license": "ISC",
|
| 1319 |
+
"optional": true
|
| 1320 |
+
},
|
| 1321 |
+
"node_modules/shebang-command": {
|
| 1322 |
+
"version": "2.0.0",
|
| 1323 |
+
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
| 1324 |
+
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
| 1325 |
+
"license": "MIT",
|
| 1326 |
+
"optional": true,
|
| 1327 |
+
"dependencies": {
|
| 1328 |
+
"shebang-regex": "^3.0.0"
|
| 1329 |
+
},
|
| 1330 |
+
"engines": {
|
| 1331 |
+
"node": ">=8"
|
| 1332 |
+
}
|
| 1333 |
+
},
|
| 1334 |
+
"node_modules/shebang-regex": {
|
| 1335 |
+
"version": "3.0.0",
|
| 1336 |
+
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
| 1337 |
+
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
| 1338 |
+
"license": "MIT",
|
| 1339 |
+
"optional": true,
|
| 1340 |
+
"engines": {
|
| 1341 |
+
"node": ">=8"
|
| 1342 |
+
}
|
| 1343 |
+
},
|
| 1344 |
+
"node_modules/side-channel": {
|
| 1345 |
+
"version": "1.1.0",
|
| 1346 |
+
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
| 1347 |
+
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
|
| 1348 |
+
"license": "MIT",
|
| 1349 |
+
"optional": true,
|
| 1350 |
+
"dependencies": {
|
| 1351 |
+
"es-errors": "^1.3.0",
|
| 1352 |
+
"object-inspect": "^1.13.3",
|
| 1353 |
+
"side-channel-list": "^1.0.0",
|
| 1354 |
+
"side-channel-map": "^1.0.1",
|
| 1355 |
+
"side-channel-weakmap": "^1.0.2"
|
| 1356 |
+
},
|
| 1357 |
+
"engines": {
|
| 1358 |
+
"node": ">= 0.4"
|
| 1359 |
+
},
|
| 1360 |
+
"funding": {
|
| 1361 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 1362 |
+
}
|
| 1363 |
+
},
|
| 1364 |
+
"node_modules/side-channel-list": {
|
| 1365 |
+
"version": "1.0.0",
|
| 1366 |
+
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
|
| 1367 |
+
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
|
| 1368 |
+
"license": "MIT",
|
| 1369 |
+
"optional": true,
|
| 1370 |
+
"dependencies": {
|
| 1371 |
+
"es-errors": "^1.3.0",
|
| 1372 |
+
"object-inspect": "^1.13.3"
|
| 1373 |
+
},
|
| 1374 |
+
"engines": {
|
| 1375 |
+
"node": ">= 0.4"
|
| 1376 |
+
},
|
| 1377 |
+
"funding": {
|
| 1378 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 1379 |
+
}
|
| 1380 |
+
},
|
| 1381 |
+
"node_modules/side-channel-map": {
|
| 1382 |
+
"version": "1.0.1",
|
| 1383 |
+
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
|
| 1384 |
+
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
|
| 1385 |
+
"license": "MIT",
|
| 1386 |
+
"optional": true,
|
| 1387 |
+
"dependencies": {
|
| 1388 |
+
"call-bound": "^1.0.2",
|
| 1389 |
+
"es-errors": "^1.3.0",
|
| 1390 |
+
"get-intrinsic": "^1.2.5",
|
| 1391 |
+
"object-inspect": "^1.13.3"
|
| 1392 |
+
},
|
| 1393 |
+
"engines": {
|
| 1394 |
+
"node": ">= 0.4"
|
| 1395 |
+
},
|
| 1396 |
+
"funding": {
|
| 1397 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 1398 |
+
}
|
| 1399 |
+
},
|
| 1400 |
+
"node_modules/side-channel-weakmap": {
|
| 1401 |
+
"version": "1.0.2",
|
| 1402 |
+
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
|
| 1403 |
+
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
|
| 1404 |
+
"license": "MIT",
|
| 1405 |
+
"optional": true,
|
| 1406 |
+
"dependencies": {
|
| 1407 |
+
"call-bound": "^1.0.2",
|
| 1408 |
+
"es-errors": "^1.3.0",
|
| 1409 |
+
"get-intrinsic": "^1.2.5",
|
| 1410 |
+
"object-inspect": "^1.13.3",
|
| 1411 |
+
"side-channel-map": "^1.0.1"
|
| 1412 |
+
},
|
| 1413 |
+
"engines": {
|
| 1414 |
+
"node": ">= 0.4"
|
| 1415 |
+
},
|
| 1416 |
+
"funding": {
|
| 1417 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 1418 |
+
}
|
| 1419 |
+
},
|
| 1420 |
+
"node_modules/signal-exit": {
|
| 1421 |
+
"version": "4.1.0",
|
| 1422 |
+
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
| 1423 |
+
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
|
| 1424 |
+
"license": "ISC",
|
| 1425 |
+
"engines": {
|
| 1426 |
+
"node": ">=14"
|
| 1427 |
+
},
|
| 1428 |
+
"funding": {
|
| 1429 |
+
"url": "https://github.com/sponsors/isaacs"
|
| 1430 |
+
}
|
| 1431 |
+
},
|
| 1432 |
+
"node_modules/slice-ansi": {
|
| 1433 |
+
"version": "5.0.0",
|
| 1434 |
+
"resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz",
|
| 1435 |
+
"integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==",
|
| 1436 |
+
"license": "MIT",
|
| 1437 |
+
"dependencies": {
|
| 1438 |
+
"ansi-styles": "^6.0.0",
|
| 1439 |
+
"is-fullwidth-code-point": "^4.0.0"
|
| 1440 |
+
},
|
| 1441 |
+
"engines": {
|
| 1442 |
+
"node": ">=12"
|
| 1443 |
+
},
|
| 1444 |
+
"funding": {
|
| 1445 |
+
"url": "https://github.com/chalk/slice-ansi?sponsor=1"
|
| 1446 |
+
}
|
| 1447 |
+
},
|
| 1448 |
+
"node_modules/statuses": {
|
| 1449 |
+
"version": "2.0.2",
|
| 1450 |
+
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
|
| 1451 |
+
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
|
| 1452 |
+
"license": "MIT",
|
| 1453 |
+
"optional": true,
|
| 1454 |
+
"engines": {
|
| 1455 |
+
"node": ">= 0.8"
|
| 1456 |
+
}
|
| 1457 |
+
},
|
| 1458 |
+
"node_modules/string-width": {
|
| 1459 |
+
"version": "7.2.0",
|
| 1460 |
+
"resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
|
| 1461 |
+
"integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
|
| 1462 |
+
"license": "MIT",
|
| 1463 |
+
"dependencies": {
|
| 1464 |
+
"emoji-regex": "^10.3.0",
|
| 1465 |
+
"get-east-asian-width": "^1.0.0",
|
| 1466 |
+
"strip-ansi": "^7.1.0"
|
| 1467 |
+
},
|
| 1468 |
+
"engines": {
|
| 1469 |
+
"node": ">=18"
|
| 1470 |
+
},
|
| 1471 |
+
"funding": {
|
| 1472 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 1473 |
+
}
|
| 1474 |
+
},
|
| 1475 |
+
"node_modules/strip-ansi": {
|
| 1476 |
+
"version": "7.1.0",
|
| 1477 |
+
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
| 1478 |
+
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
| 1479 |
+
"license": "MIT",
|
| 1480 |
+
"dependencies": {
|
| 1481 |
+
"ansi-regex": "^6.0.1"
|
| 1482 |
+
},
|
| 1483 |
+
"engines": {
|
| 1484 |
+
"node": ">=12"
|
| 1485 |
+
},
|
| 1486 |
+
"funding": {
|
| 1487 |
+
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
| 1488 |
+
}
|
| 1489 |
+
},
|
| 1490 |
+
"node_modules/toidentifier": {
|
| 1491 |
+
"version": "1.0.1",
|
| 1492 |
+
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
| 1493 |
+
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
|
| 1494 |
+
"license": "MIT",
|
| 1495 |
+
"optional": true,
|
| 1496 |
+
"engines": {
|
| 1497 |
+
"node": ">=0.6"
|
| 1498 |
+
}
|
| 1499 |
+
},
|
| 1500 |
+
"node_modules/type-is": {
|
| 1501 |
+
"version": "2.0.1",
|
| 1502 |
+
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
|
| 1503 |
+
"integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
|
| 1504 |
+
"license": "MIT",
|
| 1505 |
+
"optional": true,
|
| 1506 |
+
"dependencies": {
|
| 1507 |
+
"content-type": "^1.0.5",
|
| 1508 |
+
"media-typer": "^1.1.0",
|
| 1509 |
+
"mime-types": "^3.0.0"
|
| 1510 |
+
},
|
| 1511 |
+
"engines": {
|
| 1512 |
+
"node": ">= 0.6"
|
| 1513 |
+
}
|
| 1514 |
+
},
|
| 1515 |
+
"node_modules/undici-types": {
|
| 1516 |
+
"version": "7.10.0",
|
| 1517 |
+
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz",
|
| 1518 |
+
"integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==",
|
| 1519 |
+
"license": "MIT"
|
| 1520 |
+
},
|
| 1521 |
+
"node_modules/unpipe": {
|
| 1522 |
+
"version": "1.0.0",
|
| 1523 |
+
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
| 1524 |
+
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
|
| 1525 |
+
"license": "MIT",
|
| 1526 |
+
"optional": true,
|
| 1527 |
+
"engines": {
|
| 1528 |
+
"node": ">= 0.8"
|
| 1529 |
+
}
|
| 1530 |
+
},
|
| 1531 |
+
"node_modules/uri-js": {
|
| 1532 |
+
"version": "4.4.1",
|
| 1533 |
+
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
| 1534 |
+
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
|
| 1535 |
+
"license": "BSD-2-Clause",
|
| 1536 |
+
"optional": true,
|
| 1537 |
+
"dependencies": {
|
| 1538 |
+
"punycode": "^2.1.0"
|
| 1539 |
+
}
|
| 1540 |
+
},
|
| 1541 |
+
"node_modules/vary": {
|
| 1542 |
+
"version": "1.1.2",
|
| 1543 |
+
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
| 1544 |
+
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
| 1545 |
+
"license": "MIT",
|
| 1546 |
+
"optional": true,
|
| 1547 |
+
"engines": {
|
| 1548 |
+
"node": ">= 0.8"
|
| 1549 |
+
}
|
| 1550 |
+
},
|
| 1551 |
+
"node_modules/which": {
|
| 1552 |
+
"version": "2.0.2",
|
| 1553 |
+
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
| 1554 |
+
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
| 1555 |
+
"license": "ISC",
|
| 1556 |
+
"optional": true,
|
| 1557 |
+
"dependencies": {
|
| 1558 |
+
"isexe": "^2.0.0"
|
| 1559 |
+
},
|
| 1560 |
+
"bin": {
|
| 1561 |
+
"node-which": "bin/node-which"
|
| 1562 |
+
},
|
| 1563 |
+
"engines": {
|
| 1564 |
+
"node": ">= 8"
|
| 1565 |
+
}
|
| 1566 |
+
},
|
| 1567 |
+
"node_modules/wrap-ansi": {
|
| 1568 |
+
"version": "9.0.0",
|
| 1569 |
+
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz",
|
| 1570 |
+
"integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==",
|
| 1571 |
+
"license": "MIT",
|
| 1572 |
+
"dependencies": {
|
| 1573 |
+
"ansi-styles": "^6.2.1",
|
| 1574 |
+
"string-width": "^7.0.0",
|
| 1575 |
+
"strip-ansi": "^7.1.0"
|
| 1576 |
+
},
|
| 1577 |
+
"engines": {
|
| 1578 |
+
"node": ">=18"
|
| 1579 |
+
},
|
| 1580 |
+
"funding": {
|
| 1581 |
+
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
| 1582 |
+
}
|
| 1583 |
+
},
|
| 1584 |
+
"node_modules/wrappy": {
|
| 1585 |
+
"version": "1.0.2",
|
| 1586 |
+
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
| 1587 |
+
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
| 1588 |
+
"license": "ISC",
|
| 1589 |
+
"optional": true
|
| 1590 |
+
},
|
| 1591 |
+
"node_modules/ws": {
|
| 1592 |
+
"version": "8.18.3",
|
| 1593 |
+
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
| 1594 |
+
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
|
| 1595 |
+
"license": "MIT",
|
| 1596 |
+
"engines": {
|
| 1597 |
+
"node": ">=10.0.0"
|
| 1598 |
+
},
|
| 1599 |
+
"peerDependencies": {
|
| 1600 |
+
"bufferutil": "^4.0.1",
|
| 1601 |
+
"utf-8-validate": ">=5.0.2"
|
| 1602 |
+
},
|
| 1603 |
+
"peerDependenciesMeta": {
|
| 1604 |
+
"bufferutil": {
|
| 1605 |
+
"optional": true
|
| 1606 |
+
},
|
| 1607 |
+
"utf-8-validate": {
|
| 1608 |
+
"optional": true
|
| 1609 |
+
}
|
| 1610 |
+
}
|
| 1611 |
+
},
|
| 1612 |
+
"node_modules/zod": {
|
| 1613 |
+
"version": "3.25.67",
|
| 1614 |
+
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz",
|
| 1615 |
+
"integrity": "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==",
|
| 1616 |
+
"license": "MIT",
|
| 1617 |
+
"optional": true,
|
| 1618 |
+
"funding": {
|
| 1619 |
+
"url": "https://github.com/sponsors/colinhacks"
|
| 1620 |
+
}
|
| 1621 |
+
},
|
| 1622 |
+
"node_modules/zod-to-json-schema": {
|
| 1623 |
+
"version": "3.24.6",
|
| 1624 |
+
"resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz",
|
| 1625 |
+
"integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==",
|
| 1626 |
+
"license": "ISC",
|
| 1627 |
+
"optional": true,
|
| 1628 |
+
"peerDependencies": {
|
| 1629 |
+
"zod": "^3.24.1"
|
| 1630 |
+
}
|
| 1631 |
+
}
|
| 1632 |
+
}
|
| 1633 |
+
}
|
compatibility-test/package.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"type": "module",
|
| 3 |
+
"dependencies": {
|
| 4 |
+
"@openai/agents": "^0.0.15",
|
| 5 |
+
"ajv": "^8.17.1",
|
| 6 |
+
"listr2": "^9.0.1"
|
| 7 |
+
},
|
| 8 |
+
"scripts": {
|
| 9 |
+
"start": "tsx index.ts"
|
| 10 |
+
}
|
| 11 |
+
}
|
compatibility-test/providers.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
export const PROVIDERS = {
|
| 2 |
+
vllm: {
|
| 3 |
+
apiBaseUrl: "http://localhost:8000/v1",
|
| 4 |
+
apiKey: "vllm",
|
| 5 |
+
apiType: ["responses", "chat"], // choose from responses, chat, or both
|
| 6 |
+
modelName: "openai/gpt-oss-120b",
|
| 7 |
+
providerDetails: {
|
| 8 |
+
// add any provider-specific details here. These will be passed as part of every request
|
| 9 |
+
// for example to fix the provider for openrouter, you can do:
|
| 10 |
+
// provider: {
|
| 11 |
+
// only: ["example"],
|
| 12 |
+
// },
|
| 13 |
+
},
|
| 14 |
+
},
|
| 15 |
+
};
|
compatibility-test/runCase.ts
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import {
|
| 2 |
+
Agent,
|
| 3 |
+
Runner,
|
| 4 |
+
OpenAIResponsesModel,
|
| 5 |
+
OpenAIChatCompletionsModel,
|
| 6 |
+
RunResult,
|
| 7 |
+
StreamedRunResult,
|
| 8 |
+
FunctionTool,
|
| 9 |
+
setTracingDisabled,
|
| 10 |
+
} from "@openai/agents";
|
| 11 |
+
import { Ajv } from "ajv";
|
| 12 |
+
import { OpenAI } from "openai";
|
| 13 |
+
import { PROVIDERS } from "./providers";
|
| 14 |
+
import { TOOLS_MAP } from "./tools";
|
| 15 |
+
|
| 16 |
+
setTracingDisabled(true);
|
| 17 |
+
|
| 18 |
+
const ajv = new Ajv();
|
| 19 |
+
|
| 20 |
+
export type Case = {
|
| 21 |
+
tool_name: string;
|
| 22 |
+
input: string;
|
| 23 |
+
expected_arguments: string;
|
| 24 |
+
instructions?: string;
|
| 25 |
+
};
|
| 26 |
+
|
| 27 |
+
// Summary shape for each apiType
|
| 28 |
+
export type RunCaseSummary = {
|
| 29 |
+
apiType: string;
|
| 30 |
+
success: boolean;
|
| 31 |
+
validResponse: boolean;
|
| 32 |
+
validEvents?: boolean;
|
| 33 |
+
details: Record<string, any>;
|
| 34 |
+
history: any[];
|
| 35 |
+
successToolCall: boolean;
|
| 36 |
+
toolCallingDetails: Record<string, any>;
|
| 37 |
+
};
|
| 38 |
+
|
| 39 |
+
export async function runCase(
|
| 40 |
+
provider: string,
|
| 41 |
+
caseData: Case,
|
| 42 |
+
{
|
| 43 |
+
maxTurns,
|
| 44 |
+
streaming,
|
| 45 |
+
strict,
|
| 46 |
+
}: { maxTurns: number; streaming: boolean; strict: boolean }
|
| 47 |
+
): Promise<RunCaseSummary[]> {
|
| 48 |
+
const config = PROVIDERS[provider];
|
| 49 |
+
if (!config) {
|
| 50 |
+
throw new Error(
|
| 51 |
+
`Provider ${provider} not found. Valid providers are: ${Object.keys(
|
| 52 |
+
PROVIDERS
|
| 53 |
+
).join(", ")}`
|
| 54 |
+
);
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
const agent = new Agent({
|
| 58 |
+
name: caseData.tool_name,
|
| 59 |
+
instructions: caseData.instructions,
|
| 60 |
+
tools: [TOOLS_MAP[caseData.tool_name]],
|
| 61 |
+
});
|
| 62 |
+
|
| 63 |
+
const client = new OpenAI({
|
| 64 |
+
apiKey: config.apiKey,
|
| 65 |
+
baseURL: config.apiBaseUrl,
|
| 66 |
+
});
|
| 67 |
+
|
| 68 |
+
const summaries: RunCaseSummary[] = [];
|
| 69 |
+
|
| 70 |
+
for (const apiType of config.apiType) {
|
| 71 |
+
const runner = new Runner({
|
| 72 |
+
model:
|
| 73 |
+
apiType === "responses"
|
| 74 |
+
? new OpenAIResponsesModel(client, config.modelName)
|
| 75 |
+
: new OpenAIChatCompletionsModel(client, config.modelName),
|
| 76 |
+
modelSettings: {
|
| 77 |
+
providerData: config.providerDetails ?? {},
|
| 78 |
+
},
|
| 79 |
+
});
|
| 80 |
+
|
| 81 |
+
let result: RunResult<any, any> | StreamedRunResult<any, any>;
|
| 82 |
+
let streamedEvents: any[] | undefined = undefined;
|
| 83 |
+
if (streaming) {
|
| 84 |
+
result = await runner.run(agent, caseData.input, {
|
| 85 |
+
stream: streaming,
|
| 86 |
+
maxTurns: maxTurns,
|
| 87 |
+
});
|
| 88 |
+
if (result instanceof StreamedRunResult) {
|
| 89 |
+
// Collect streaming events if applicable
|
| 90 |
+
streamedEvents = [];
|
| 91 |
+
for await (const event of result) {
|
| 92 |
+
if (event.type === "raw_model_stream_event") {
|
| 93 |
+
if (event.data.type === "model") {
|
| 94 |
+
streamedEvents.push(event.data.event);
|
| 95 |
+
}
|
| 96 |
+
}
|
| 97 |
+
}
|
| 98 |
+
await result.completed;
|
| 99 |
+
}
|
| 100 |
+
} else {
|
| 101 |
+
result = await runner.run(agent, caseData.input, {
|
| 102 |
+
maxTurns: maxTurns,
|
| 103 |
+
});
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
const { success: successToolCall, details: toolCallingDetails } =
|
| 107 |
+
testToolCall(apiType, caseData, result, strict);
|
| 108 |
+
|
| 109 |
+
const { validResponse, details } = testOutputData(
|
| 110 |
+
apiType,
|
| 111 |
+
result.rawResponses,
|
| 112 |
+
streaming
|
| 113 |
+
);
|
| 114 |
+
|
| 115 |
+
const { validEvents, details: eventsDetails } = streaming
|
| 116 |
+
? testEvents(apiType, streamedEvents)
|
| 117 |
+
: { validEvents: true, details: {} };
|
| 118 |
+
|
| 119 |
+
let success = successToolCall && validResponse;
|
| 120 |
+
if (streaming) {
|
| 121 |
+
success = success && validEvents;
|
| 122 |
+
}
|
| 123 |
+
const summary: RunCaseSummary = {
|
| 124 |
+
apiType,
|
| 125 |
+
success,
|
| 126 |
+
validResponse,
|
| 127 |
+
validEvents,
|
| 128 |
+
details: {
|
| 129 |
+
...details,
|
| 130 |
+
...eventsDetails,
|
| 131 |
+
},
|
| 132 |
+
history: result?.rawResponses.map((entry) => entry.providerData) ?? [],
|
| 133 |
+
successToolCall,
|
| 134 |
+
toolCallingDetails,
|
| 135 |
+
};
|
| 136 |
+
|
| 137 |
+
summaries.push(summary);
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
return summaries;
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
function testToolCall(apiType, caseData, result, strict) {
|
| 144 |
+
let details: Record<string, boolean | string> = {};
|
| 145 |
+
result.newItems.forEach((item) => {
|
| 146 |
+
// for this test for now we only care if the tool is called at least once
|
| 147 |
+
if (details.calledToolAtLeastOnce) {
|
| 148 |
+
return;
|
| 149 |
+
}
|
| 150 |
+
|
| 151 |
+
const isToolCall = item.type === "tool_call_item";
|
| 152 |
+
if (isToolCall) {
|
| 153 |
+
if (item.rawItem.type === "function_call") {
|
| 154 |
+
if (item.rawItem.name === caseData.tool_name) {
|
| 155 |
+
const validate = ajv.compile(
|
| 156 |
+
(TOOLS_MAP[caseData.tool_name] as FunctionTool).parameters
|
| 157 |
+
);
|
| 158 |
+
const valid = validate(JSON.parse(item.rawItem.arguments));
|
| 159 |
+
details.calledToolWithRightSchema = valid;
|
| 160 |
+
details.calledToolAtLeastOnce = true;
|
| 161 |
+
|
| 162 |
+
if (details.calledToolWithRightSchema) {
|
| 163 |
+
const parsedArguments = JSON.parse(item.rawItem.arguments);
|
| 164 |
+
const expectedArguments = JSON.parse(caseData.expected_arguments);
|
| 165 |
+
details.calledToolWithRightArguments = deepEqual(
|
| 166 |
+
parsedArguments,
|
| 167 |
+
expectedArguments
|
| 168 |
+
);
|
| 169 |
+
if (!details.calledToolWithRightArguments) {
|
| 170 |
+
if (details.calledToolWithRightSchema) {
|
| 171 |
+
details.warning = `Tool call with wrong arguments but correct schema. Check logs for full details. Not failing this test. Parsed: ${JSON.stringify(
|
| 172 |
+
parsedArguments
|
| 173 |
+
)} Expected: ${JSON.stringify(expectedArguments)}`;
|
| 174 |
+
}
|
| 175 |
+
details.actualArguments = parsedArguments;
|
| 176 |
+
details.expectedArguments = expectedArguments;
|
| 177 |
+
}
|
| 178 |
+
}
|
| 179 |
+
}
|
| 180 |
+
}
|
| 181 |
+
}
|
| 182 |
+
});
|
| 183 |
+
|
| 184 |
+
return {
|
| 185 |
+
success:
|
| 186 |
+
!!details.calledToolAtLeastOnce &&
|
| 187 |
+
!!details.calledToolWithRightSchema &&
|
| 188 |
+
(!strict || !!details.calledToolWithRightArguments),
|
| 189 |
+
details,
|
| 190 |
+
};
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
function testEvents(apiType, events) {
|
| 194 |
+
// In an ideal world we would check all the events to follow and reconstruct the final response
|
| 195 |
+
// and then compare it against the final response in the response.completed event
|
| 196 |
+
// for now we just check that certain events are present
|
| 197 |
+
|
| 198 |
+
let details: Record<string, boolean> = {};
|
| 199 |
+
let validEvents: boolean = false;
|
| 200 |
+
|
| 201 |
+
if (apiType === "chat") {
|
| 202 |
+
let hasReasoningDeltas = false;
|
| 203 |
+
for (const event of events) {
|
| 204 |
+
hasReasoningDeltas =
|
| 205 |
+
hasReasoningDeltas ||
|
| 206 |
+
(typeof event.choices[0].delta.reasoning === "string" &&
|
| 207 |
+
event.choices[0].delta.reasoning.length > 0);
|
| 208 |
+
}
|
| 209 |
+
details.hasReasoningDeltas = hasReasoningDeltas;
|
| 210 |
+
validEvents = hasReasoningDeltas;
|
| 211 |
+
}
|
| 212 |
+
|
| 213 |
+
if (apiType === "responses") {
|
| 214 |
+
let hasReasoningDeltaEvents = false;
|
| 215 |
+
let hasReasoningDoneEvents = false;
|
| 216 |
+
for (const event of events) {
|
| 217 |
+
if (event.type === "raw_model_stream_event") {
|
| 218 |
+
if (event.data.type === "model") {
|
| 219 |
+
if (event.data.event.type === "response.reasoning_text.delta") {
|
| 220 |
+
hasReasoningDeltaEvents = true;
|
| 221 |
+
}
|
| 222 |
+
if (event.data.event.type === "response.reasoning_text.done") {
|
| 223 |
+
hasReasoningDoneEvents = true;
|
| 224 |
+
}
|
| 225 |
+
}
|
| 226 |
+
}
|
| 227 |
+
}
|
| 228 |
+
|
| 229 |
+
details.hasReasoningDeltaEvents = hasReasoningDeltaEvents;
|
| 230 |
+
details.hasReasoningDoneEvents = hasReasoningDoneEvents;
|
| 231 |
+
validEvents =
|
| 232 |
+
details.hasReasoningDeltaEvents && details.hasReasoningDoneEvents;
|
| 233 |
+
}
|
| 234 |
+
|
| 235 |
+
return {
|
| 236 |
+
validEvents,
|
| 237 |
+
details,
|
| 238 |
+
};
|
| 239 |
+
}
|
| 240 |
+
|
| 241 |
+
function testOutputData(apiType, rawResponses, streaming) {
|
| 242 |
+
let details: Record<string, boolean> = {};
|
| 243 |
+
let validResponse: boolean = false;
|
| 244 |
+
|
| 245 |
+
if (apiType === "chat") {
|
| 246 |
+
for (const response of rawResponses) {
|
| 247 |
+
if (streaming && !response.providerData) {
|
| 248 |
+
// with Chat Completions we don't have a final response object that's native so we skip this test
|
| 249 |
+
return {
|
| 250 |
+
validResponse: true,
|
| 251 |
+
details: {
|
| 252 |
+
skippedBecauseStreaming: true,
|
| 253 |
+
},
|
| 254 |
+
};
|
| 255 |
+
}
|
| 256 |
+
|
| 257 |
+
// this is the actual HTTP response from the provider
|
| 258 |
+
// Since it's not guaranteed that every response has a reasoning field, we check if it's present
|
| 259 |
+
// at least once across all responses
|
| 260 |
+
const data = response.providerData;
|
| 261 |
+
const message = data.choices[0].message;
|
| 262 |
+
if (message.role === "assistant" && !message.refusal) {
|
| 263 |
+
details.hasReasoningField =
|
| 264 |
+
details.hasReasoningField ||
|
| 265 |
+
("reasoning" in message && typeof message.reasoning === "string");
|
| 266 |
+
details.hasReasoningContentField =
|
| 267 |
+
details.hasReasoningContentField ||
|
| 268 |
+
("reasoning_content" in message &&
|
| 269 |
+
typeof message.reasoning_content === "string");
|
| 270 |
+
|
| 271 |
+
validResponse =
|
| 272 |
+
validResponse ||
|
| 273 |
+
(details.hasReasoningField && message.reasoning.length > 0);
|
| 274 |
+
}
|
| 275 |
+
}
|
| 276 |
+
} else if (apiType === "responses") {
|
| 277 |
+
// this is the actual HTTP response from the provider
|
| 278 |
+
const data = rawResponses[0].providerData;
|
| 279 |
+
for (const item of data.output) {
|
| 280 |
+
// Since it's not guaranteed that every response has a reasoning field, we check if it's present
|
| 281 |
+
// at least once across all responses
|
| 282 |
+
|
| 283 |
+
if (item.type === "reasoning") {
|
| 284 |
+
details.hasReasoningContentArray = Array.isArray(item.content);
|
| 285 |
+
details.hasReasoningContentArrayLength = item.content.length > 0;
|
| 286 |
+
details.hasReasoningContentArrayItemType = item.content.every(
|
| 287 |
+
(item) => item.type === "reasoning_text"
|
| 288 |
+
);
|
| 289 |
+
details.hasReasoningContentArrayItemText = item.content.every(
|
| 290 |
+
(item) => item.text.length > 0
|
| 291 |
+
);
|
| 292 |
+
|
| 293 |
+
validResponse =
|
| 294 |
+
details.hasReasoningContentArray &&
|
| 295 |
+
details.hasReasoningContentArrayLength &&
|
| 296 |
+
details.hasReasoningContentArrayItemType &&
|
| 297 |
+
details.hasReasoningContentArrayItemText;
|
| 298 |
+
}
|
| 299 |
+
}
|
| 300 |
+
}
|
| 301 |
+
|
| 302 |
+
return {
|
| 303 |
+
validResponse,
|
| 304 |
+
details,
|
| 305 |
+
};
|
| 306 |
+
}
|
| 307 |
+
|
| 308 |
+
function deepEqual(a: any, b: any): boolean {
|
| 309 |
+
if (a === b) return true;
|
| 310 |
+
if (typeof a !== typeof b) return false;
|
| 311 |
+
if (a && b && typeof a === "object") {
|
| 312 |
+
if (Array.isArray(a) !== Array.isArray(b)) return false;
|
| 313 |
+
if (Array.isArray(a)) {
|
| 314 |
+
if (a.length !== b.length) return false;
|
| 315 |
+
for (let i = 0; i < a.length; i++) {
|
| 316 |
+
if (!deepEqual(a[i], b[i])) return false;
|
| 317 |
+
}
|
| 318 |
+
return true;
|
| 319 |
+
} else {
|
| 320 |
+
const aKeys = Object.keys(a);
|
| 321 |
+
const bKeys = Object.keys(b);
|
| 322 |
+
if (aKeys.length !== bKeys.length) return false;
|
| 323 |
+
for (const key of aKeys) {
|
| 324 |
+
if (!b.hasOwnProperty(key)) return false;
|
| 325 |
+
if (!deepEqual(a[key], b[key])) return false;
|
| 326 |
+
}
|
| 327 |
+
return true;
|
| 328 |
+
}
|
| 329 |
+
}
|
| 330 |
+
return false;
|
| 331 |
+
}
|
compatibility-test/tools.ts
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { Tool, tool } from "@openai/agents";
|
| 2 |
+
|
| 3 |
+
function convertToTool(toolData: any) {
|
| 4 |
+
return tool({
|
| 5 |
+
name: toolData.name,
|
| 6 |
+
description: toolData.description,
|
| 7 |
+
parameters: toolData.parameters,
|
| 8 |
+
execute: async (parameters) => {
|
| 9 |
+
return toolData.output;
|
| 10 |
+
},
|
| 11 |
+
strict: false,
|
| 12 |
+
});
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
export const TOOLS = [
|
| 16 |
+
{
|
| 17 |
+
type: "function",
|
| 18 |
+
name: "get_weather",
|
| 19 |
+
description: "Get the weather for a given location",
|
| 20 |
+
parameters: {
|
| 21 |
+
type: "object",
|
| 22 |
+
properties: {
|
| 23 |
+
location: {
|
| 24 |
+
type: "string",
|
| 25 |
+
description: "The location to get the weather for",
|
| 26 |
+
},
|
| 27 |
+
},
|
| 28 |
+
required: ["location"],
|
| 29 |
+
additionalProperties: false,
|
| 30 |
+
},
|
| 31 |
+
output: '{"weather":"sunny"}',
|
| 32 |
+
},
|
| 33 |
+
{
|
| 34 |
+
type: "function",
|
| 35 |
+
name: "get_system_health",
|
| 36 |
+
description:
|
| 37 |
+
"Returns the current health status of the LLM runtime—use before critical operations to verify the service is live.",
|
| 38 |
+
parameters: { type: "object", properties: {} },
|
| 39 |
+
output: '{"status":"ok","uptime_seconds":372045}',
|
| 40 |
+
},
|
| 41 |
+
{
|
| 42 |
+
type: "function",
|
| 43 |
+
name: "markdown_to_html",
|
| 44 |
+
description:
|
| 45 |
+
"Converts a Markdown string to sanitized HTML—use when you need browser-renderable output.",
|
| 46 |
+
parameters: {
|
| 47 |
+
type: "object",
|
| 48 |
+
properties: {
|
| 49 |
+
markdown: { type: "string", description: "Raw Markdown content" },
|
| 50 |
+
},
|
| 51 |
+
required: ["markdown"],
|
| 52 |
+
additionalProperties: false,
|
| 53 |
+
},
|
| 54 |
+
output: '{"html":"<h1>Hello World</h1><p>This is <em>great</em>.</p>"}',
|
| 55 |
+
},
|
| 56 |
+
{
|
| 57 |
+
type: "function",
|
| 58 |
+
name: "detect_language",
|
| 59 |
+
description:
|
| 60 |
+
"Identifies the ISO language code of the supplied text—use for routing text to language-specific models.",
|
| 61 |
+
parameters: {
|
| 62 |
+
type: "object",
|
| 63 |
+
properties: {
|
| 64 |
+
text: {
|
| 65 |
+
type: "string",
|
| 66 |
+
description: "Text whose language should be detected",
|
| 67 |
+
},
|
| 68 |
+
},
|
| 69 |
+
required: ["text"],
|
| 70 |
+
additionalProperties: false,
|
| 71 |
+
},
|
| 72 |
+
output: '{"language":"de","confidence":0.98}',
|
| 73 |
+
},
|
| 74 |
+
{
|
| 75 |
+
type: "function",
|
| 76 |
+
name: "generate_chart",
|
| 77 |
+
description:
|
| 78 |
+
"Creates a base64-encoded PNG chart from tabular data—use for quick visualizations inside chat.",
|
| 79 |
+
parameters: {
|
| 80 |
+
type: "object",
|
| 81 |
+
properties: {
|
| 82 |
+
data: {
|
| 83 |
+
type: "array",
|
| 84 |
+
items: { type: "array", items: { type: "number" } },
|
| 85 |
+
description: "2-D numeric data matrix",
|
| 86 |
+
},
|
| 87 |
+
chart_type: {
|
| 88 |
+
type: "string",
|
| 89 |
+
enum: ["line", "bar", "scatter"],
|
| 90 |
+
description: "Type of chart to generate",
|
| 91 |
+
},
|
| 92 |
+
title: {
|
| 93 |
+
type: "string",
|
| 94 |
+
description: "Chart title",
|
| 95 |
+
default: "",
|
| 96 |
+
},
|
| 97 |
+
x_label: {
|
| 98 |
+
type: "string",
|
| 99 |
+
description: "Label for the x-axis",
|
| 100 |
+
default: "",
|
| 101 |
+
},
|
| 102 |
+
y_label: {
|
| 103 |
+
type: "string",
|
| 104 |
+
description: "Label for the y-axis",
|
| 105 |
+
default: "",
|
| 106 |
+
},
|
| 107 |
+
},
|
| 108 |
+
required: ["data", "chart_type"],
|
| 109 |
+
additionalProperties: false,
|
| 110 |
+
},
|
| 111 |
+
output: '{"image_png_base64":"iVBORw0KGgoAAAANSUhEUgAA..."}',
|
| 112 |
+
},
|
| 113 |
+
{
|
| 114 |
+
type: "function",
|
| 115 |
+
name: "query_database",
|
| 116 |
+
description:
|
| 117 |
+
"Runs a parameterized SQL SELECT on the internal analytics DB—use for lightweight data look-ups.",
|
| 118 |
+
parameters: {
|
| 119 |
+
type: "object",
|
| 120 |
+
properties: {
|
| 121 |
+
table: { type: "string", description: "Table name to query" },
|
| 122 |
+
columns: {
|
| 123 |
+
type: "array",
|
| 124 |
+
items: { type: "string" },
|
| 125 |
+
description: "Columns to return",
|
| 126 |
+
},
|
| 127 |
+
filters: {
|
| 128 |
+
type: "string",
|
| 129 |
+
description: "SQL WHERE clause without the word WHERE",
|
| 130 |
+
default: "",
|
| 131 |
+
},
|
| 132 |
+
limit: {
|
| 133 |
+
type: "integer",
|
| 134 |
+
minimum: 1,
|
| 135 |
+
maximum: 10000,
|
| 136 |
+
description: "Max rows to return",
|
| 137 |
+
default: 100,
|
| 138 |
+
},
|
| 139 |
+
order_by: {
|
| 140 |
+
type: "string",
|
| 141 |
+
description: "Column to order by (optional)",
|
| 142 |
+
default: "",
|
| 143 |
+
},
|
| 144 |
+
},
|
| 145 |
+
required: ["table", "columns"],
|
| 146 |
+
additionalProperties: false,
|
| 147 |
+
},
|
| 148 |
+
output:
|
| 149 |
+
'{"rows":[{"id":1,"email":"user@example.com"},{"id":2,"email":"foo@bar.com"}],"row_count":2}',
|
| 150 |
+
},
|
| 151 |
+
];
|
| 152 |
+
|
| 153 |
+
export const TOOLS_MAP = TOOLS.reduce((acc, tool) => {
|
| 154 |
+
acc[tool.name] = convertToTool(tool);
|
| 155 |
+
return acc;
|
| 156 |
+
}, {} as Record<string, Tool>);
|
docs/gpt-oss-120b.svg
ADDED
|
|
Git LFS Details
|
docs/gpt-oss-20b.svg
ADDED
|
|
Git LFS Details
|
docs/gpt-oss.svg
ADDED
|
|
Git LFS Details
|
examples/agents-sdk-js/index.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { OpenAI } from "openai";
|
| 2 |
+
import {
|
| 3 |
+
Agent,
|
| 4 |
+
run,
|
| 5 |
+
setDefaultOpenAIClient,
|
| 6 |
+
setOpenAIAPI,
|
| 7 |
+
setTracingDisabled,
|
| 8 |
+
tool,
|
| 9 |
+
MCPServerStdio,
|
| 10 |
+
} from "@openai/agents";
|
| 11 |
+
import { z } from "zod";
|
| 12 |
+
import path from "node:path";
|
| 13 |
+
import process from "node:process";
|
| 14 |
+
import { styleText } from "node:util";
|
| 15 |
+
import { createInterface } from "node:readline/promises";
|
| 16 |
+
|
| 17 |
+
async function prompt(question: string) {
|
| 18 |
+
const rl = createInterface({
|
| 19 |
+
input: process.stdin,
|
| 20 |
+
output: process.stdout,
|
| 21 |
+
});
|
| 22 |
+
const answer = await rl.question(question);
|
| 23 |
+
rl.close();
|
| 24 |
+
return answer;
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
const openai = new OpenAI({
|
| 28 |
+
apiKey: "local",
|
| 29 |
+
baseURL: "http://localhost:11434/v1",
|
| 30 |
+
});
|
| 31 |
+
|
| 32 |
+
const samplesDir = path.join(process.cwd());
|
| 33 |
+
|
| 34 |
+
const mcpServer = new MCPServerStdio({
|
| 35 |
+
name: "Filesystem MCP Server, via npx",
|
| 36 |
+
fullCommand: `npx -y @modelcontextprotocol/server-filesystem ${samplesDir}`,
|
| 37 |
+
});
|
| 38 |
+
|
| 39 |
+
await mcpServer.connect();
|
| 40 |
+
|
| 41 |
+
setTracingDisabled(true);
|
| 42 |
+
setDefaultOpenAIClient(openai);
|
| 43 |
+
setOpenAIAPI("chat_completions");
|
| 44 |
+
|
| 45 |
+
const searchTool = tool({
|
| 46 |
+
name: "get_current_weather",
|
| 47 |
+
description: "Get the current weather in a given location",
|
| 48 |
+
parameters: z.object({
|
| 49 |
+
location: z.string(),
|
| 50 |
+
}),
|
| 51 |
+
execute: async ({ location }) => {
|
| 52 |
+
return `The weather in ${location} is sunny.`;
|
| 53 |
+
},
|
| 54 |
+
});
|
| 55 |
+
|
| 56 |
+
const agent = new Agent({
|
| 57 |
+
name: "My Agent",
|
| 58 |
+
instructions: "You are a helpful assistant.",
|
| 59 |
+
tools: [searchTool],
|
| 60 |
+
model: "gpt-oss:20b-test",
|
| 61 |
+
mcpServers: [mcpServer],
|
| 62 |
+
});
|
| 63 |
+
|
| 64 |
+
const input = await prompt("> ");
|
| 65 |
+
|
| 66 |
+
const result = await run(agent, input, {
|
| 67 |
+
stream: true,
|
| 68 |
+
});
|
| 69 |
+
|
| 70 |
+
for await (const event of result) {
|
| 71 |
+
if (event.type === "raw_model_stream_event" && event.data.type === "model") {
|
| 72 |
+
if (event.data.event.choices[0].delta.content) {
|
| 73 |
+
process.stdout.write(event.data.event.choices[0].delta.content);
|
| 74 |
+
} else if (event.data.event.choices[0].delta.reasoning) {
|
| 75 |
+
process.stdout.write(event.data.event.choices[0].delta.reasoning);
|
| 76 |
+
}
|
| 77 |
+
} else if (
|
| 78 |
+
event.type === "run_item_stream_event" &&
|
| 79 |
+
event.item.type === "tool_call_item" &&
|
| 80 |
+
event.item.rawItem.type == "function_call"
|
| 81 |
+
) {
|
| 82 |
+
console.log(
|
| 83 |
+
`\nCalling ${event.item.rawItem.name} with: ${event.item.rawItem.arguments}`
|
| 84 |
+
);
|
| 85 |
+
}
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
console.log("\n");
|
| 89 |
+
await result.completed;
|
| 90 |
+
await mcpServer.close();
|
examples/agents-sdk-js/package-lock.json
ADDED
|
@@ -0,0 +1,1798 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "agents-sdk",
|
| 3 |
+
"version": "1.0.0",
|
| 4 |
+
"lockfileVersion": 3,
|
| 5 |
+
"requires": true,
|
| 6 |
+
"packages": {
|
| 7 |
+
"": {
|
| 8 |
+
"name": "agents-sdk",
|
| 9 |
+
"version": "1.0.0",
|
| 10 |
+
"license": "ISC",
|
| 11 |
+
"dependencies": {
|
| 12 |
+
"@openai/agents": "^0.0.14",
|
| 13 |
+
"tsx": "^4.20.3",
|
| 14 |
+
"typescript": "^5.8.3",
|
| 15 |
+
"zod": "^3.25.67"
|
| 16 |
+
}
|
| 17 |
+
},
|
| 18 |
+
"node_modules/@esbuild/aix-ppc64": {
|
| 19 |
+
"version": "0.25.8",
|
| 20 |
+
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz",
|
| 21 |
+
"integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==",
|
| 22 |
+
"cpu": [
|
| 23 |
+
"ppc64"
|
| 24 |
+
],
|
| 25 |
+
"license": "MIT",
|
| 26 |
+
"optional": true,
|
| 27 |
+
"os": [
|
| 28 |
+
"aix"
|
| 29 |
+
],
|
| 30 |
+
"engines": {
|
| 31 |
+
"node": ">=18"
|
| 32 |
+
}
|
| 33 |
+
},
|
| 34 |
+
"node_modules/@esbuild/android-arm": {
|
| 35 |
+
"version": "0.25.8",
|
| 36 |
+
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz",
|
| 37 |
+
"integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==",
|
| 38 |
+
"cpu": [
|
| 39 |
+
"arm"
|
| 40 |
+
],
|
| 41 |
+
"license": "MIT",
|
| 42 |
+
"optional": true,
|
| 43 |
+
"os": [
|
| 44 |
+
"android"
|
| 45 |
+
],
|
| 46 |
+
"engines": {
|
| 47 |
+
"node": ">=18"
|
| 48 |
+
}
|
| 49 |
+
},
|
| 50 |
+
"node_modules/@esbuild/android-arm64": {
|
| 51 |
+
"version": "0.25.8",
|
| 52 |
+
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz",
|
| 53 |
+
"integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==",
|
| 54 |
+
"cpu": [
|
| 55 |
+
"arm64"
|
| 56 |
+
],
|
| 57 |
+
"license": "MIT",
|
| 58 |
+
"optional": true,
|
| 59 |
+
"os": [
|
| 60 |
+
"android"
|
| 61 |
+
],
|
| 62 |
+
"engines": {
|
| 63 |
+
"node": ">=18"
|
| 64 |
+
}
|
| 65 |
+
},
|
| 66 |
+
"node_modules/@esbuild/android-x64": {
|
| 67 |
+
"version": "0.25.8",
|
| 68 |
+
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz",
|
| 69 |
+
"integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==",
|
| 70 |
+
"cpu": [
|
| 71 |
+
"x64"
|
| 72 |
+
],
|
| 73 |
+
"license": "MIT",
|
| 74 |
+
"optional": true,
|
| 75 |
+
"os": [
|
| 76 |
+
"android"
|
| 77 |
+
],
|
| 78 |
+
"engines": {
|
| 79 |
+
"node": ">=18"
|
| 80 |
+
}
|
| 81 |
+
},
|
| 82 |
+
"node_modules/@esbuild/darwin-arm64": {
|
| 83 |
+
"version": "0.25.8",
|
| 84 |
+
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz",
|
| 85 |
+
"integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==",
|
| 86 |
+
"cpu": [
|
| 87 |
+
"arm64"
|
| 88 |
+
],
|
| 89 |
+
"license": "MIT",
|
| 90 |
+
"optional": true,
|
| 91 |
+
"os": [
|
| 92 |
+
"darwin"
|
| 93 |
+
],
|
| 94 |
+
"engines": {
|
| 95 |
+
"node": ">=18"
|
| 96 |
+
}
|
| 97 |
+
},
|
| 98 |
+
"node_modules/@esbuild/darwin-x64": {
|
| 99 |
+
"version": "0.25.8",
|
| 100 |
+
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz",
|
| 101 |
+
"integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==",
|
| 102 |
+
"cpu": [
|
| 103 |
+
"x64"
|
| 104 |
+
],
|
| 105 |
+
"license": "MIT",
|
| 106 |
+
"optional": true,
|
| 107 |
+
"os": [
|
| 108 |
+
"darwin"
|
| 109 |
+
],
|
| 110 |
+
"engines": {
|
| 111 |
+
"node": ">=18"
|
| 112 |
+
}
|
| 113 |
+
},
|
| 114 |
+
"node_modules/@esbuild/freebsd-arm64": {
|
| 115 |
+
"version": "0.25.8",
|
| 116 |
+
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz",
|
| 117 |
+
"integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==",
|
| 118 |
+
"cpu": [
|
| 119 |
+
"arm64"
|
| 120 |
+
],
|
| 121 |
+
"license": "MIT",
|
| 122 |
+
"optional": true,
|
| 123 |
+
"os": [
|
| 124 |
+
"freebsd"
|
| 125 |
+
],
|
| 126 |
+
"engines": {
|
| 127 |
+
"node": ">=18"
|
| 128 |
+
}
|
| 129 |
+
},
|
| 130 |
+
"node_modules/@esbuild/freebsd-x64": {
|
| 131 |
+
"version": "0.25.8",
|
| 132 |
+
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz",
|
| 133 |
+
"integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==",
|
| 134 |
+
"cpu": [
|
| 135 |
+
"x64"
|
| 136 |
+
],
|
| 137 |
+
"license": "MIT",
|
| 138 |
+
"optional": true,
|
| 139 |
+
"os": [
|
| 140 |
+
"freebsd"
|
| 141 |
+
],
|
| 142 |
+
"engines": {
|
| 143 |
+
"node": ">=18"
|
| 144 |
+
}
|
| 145 |
+
},
|
| 146 |
+
"node_modules/@esbuild/linux-arm": {
|
| 147 |
+
"version": "0.25.8",
|
| 148 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz",
|
| 149 |
+
"integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==",
|
| 150 |
+
"cpu": [
|
| 151 |
+
"arm"
|
| 152 |
+
],
|
| 153 |
+
"license": "MIT",
|
| 154 |
+
"optional": true,
|
| 155 |
+
"os": [
|
| 156 |
+
"linux"
|
| 157 |
+
],
|
| 158 |
+
"engines": {
|
| 159 |
+
"node": ">=18"
|
| 160 |
+
}
|
| 161 |
+
},
|
| 162 |
+
"node_modules/@esbuild/linux-arm64": {
|
| 163 |
+
"version": "0.25.8",
|
| 164 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz",
|
| 165 |
+
"integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==",
|
| 166 |
+
"cpu": [
|
| 167 |
+
"arm64"
|
| 168 |
+
],
|
| 169 |
+
"license": "MIT",
|
| 170 |
+
"optional": true,
|
| 171 |
+
"os": [
|
| 172 |
+
"linux"
|
| 173 |
+
],
|
| 174 |
+
"engines": {
|
| 175 |
+
"node": ">=18"
|
| 176 |
+
}
|
| 177 |
+
},
|
| 178 |
+
"node_modules/@esbuild/linux-ia32": {
|
| 179 |
+
"version": "0.25.8",
|
| 180 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz",
|
| 181 |
+
"integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==",
|
| 182 |
+
"cpu": [
|
| 183 |
+
"ia32"
|
| 184 |
+
],
|
| 185 |
+
"license": "MIT",
|
| 186 |
+
"optional": true,
|
| 187 |
+
"os": [
|
| 188 |
+
"linux"
|
| 189 |
+
],
|
| 190 |
+
"engines": {
|
| 191 |
+
"node": ">=18"
|
| 192 |
+
}
|
| 193 |
+
},
|
| 194 |
+
"node_modules/@esbuild/linux-loong64": {
|
| 195 |
+
"version": "0.25.8",
|
| 196 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz",
|
| 197 |
+
"integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==",
|
| 198 |
+
"cpu": [
|
| 199 |
+
"loong64"
|
| 200 |
+
],
|
| 201 |
+
"license": "MIT",
|
| 202 |
+
"optional": true,
|
| 203 |
+
"os": [
|
| 204 |
+
"linux"
|
| 205 |
+
],
|
| 206 |
+
"engines": {
|
| 207 |
+
"node": ">=18"
|
| 208 |
+
}
|
| 209 |
+
},
|
| 210 |
+
"node_modules/@esbuild/linux-mips64el": {
|
| 211 |
+
"version": "0.25.8",
|
| 212 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz",
|
| 213 |
+
"integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==",
|
| 214 |
+
"cpu": [
|
| 215 |
+
"mips64el"
|
| 216 |
+
],
|
| 217 |
+
"license": "MIT",
|
| 218 |
+
"optional": true,
|
| 219 |
+
"os": [
|
| 220 |
+
"linux"
|
| 221 |
+
],
|
| 222 |
+
"engines": {
|
| 223 |
+
"node": ">=18"
|
| 224 |
+
}
|
| 225 |
+
},
|
| 226 |
+
"node_modules/@esbuild/linux-ppc64": {
|
| 227 |
+
"version": "0.25.8",
|
| 228 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz",
|
| 229 |
+
"integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==",
|
| 230 |
+
"cpu": [
|
| 231 |
+
"ppc64"
|
| 232 |
+
],
|
| 233 |
+
"license": "MIT",
|
| 234 |
+
"optional": true,
|
| 235 |
+
"os": [
|
| 236 |
+
"linux"
|
| 237 |
+
],
|
| 238 |
+
"engines": {
|
| 239 |
+
"node": ">=18"
|
| 240 |
+
}
|
| 241 |
+
},
|
| 242 |
+
"node_modules/@esbuild/linux-riscv64": {
|
| 243 |
+
"version": "0.25.8",
|
| 244 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz",
|
| 245 |
+
"integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==",
|
| 246 |
+
"cpu": [
|
| 247 |
+
"riscv64"
|
| 248 |
+
],
|
| 249 |
+
"license": "MIT",
|
| 250 |
+
"optional": true,
|
| 251 |
+
"os": [
|
| 252 |
+
"linux"
|
| 253 |
+
],
|
| 254 |
+
"engines": {
|
| 255 |
+
"node": ">=18"
|
| 256 |
+
}
|
| 257 |
+
},
|
| 258 |
+
"node_modules/@esbuild/linux-s390x": {
|
| 259 |
+
"version": "0.25.8",
|
| 260 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz",
|
| 261 |
+
"integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==",
|
| 262 |
+
"cpu": [
|
| 263 |
+
"s390x"
|
| 264 |
+
],
|
| 265 |
+
"license": "MIT",
|
| 266 |
+
"optional": true,
|
| 267 |
+
"os": [
|
| 268 |
+
"linux"
|
| 269 |
+
],
|
| 270 |
+
"engines": {
|
| 271 |
+
"node": ">=18"
|
| 272 |
+
}
|
| 273 |
+
},
|
| 274 |
+
"node_modules/@esbuild/linux-x64": {
|
| 275 |
+
"version": "0.25.8",
|
| 276 |
+
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz",
|
| 277 |
+
"integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==",
|
| 278 |
+
"cpu": [
|
| 279 |
+
"x64"
|
| 280 |
+
],
|
| 281 |
+
"license": "MIT",
|
| 282 |
+
"optional": true,
|
| 283 |
+
"os": [
|
| 284 |
+
"linux"
|
| 285 |
+
],
|
| 286 |
+
"engines": {
|
| 287 |
+
"node": ">=18"
|
| 288 |
+
}
|
| 289 |
+
},
|
| 290 |
+
"node_modules/@esbuild/netbsd-arm64": {
|
| 291 |
+
"version": "0.25.8",
|
| 292 |
+
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz",
|
| 293 |
+
"integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==",
|
| 294 |
+
"cpu": [
|
| 295 |
+
"arm64"
|
| 296 |
+
],
|
| 297 |
+
"license": "MIT",
|
| 298 |
+
"optional": true,
|
| 299 |
+
"os": [
|
| 300 |
+
"netbsd"
|
| 301 |
+
],
|
| 302 |
+
"engines": {
|
| 303 |
+
"node": ">=18"
|
| 304 |
+
}
|
| 305 |
+
},
|
| 306 |
+
"node_modules/@esbuild/netbsd-x64": {
|
| 307 |
+
"version": "0.25.8",
|
| 308 |
+
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz",
|
| 309 |
+
"integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==",
|
| 310 |
+
"cpu": [
|
| 311 |
+
"x64"
|
| 312 |
+
],
|
| 313 |
+
"license": "MIT",
|
| 314 |
+
"optional": true,
|
| 315 |
+
"os": [
|
| 316 |
+
"netbsd"
|
| 317 |
+
],
|
| 318 |
+
"engines": {
|
| 319 |
+
"node": ">=18"
|
| 320 |
+
}
|
| 321 |
+
},
|
| 322 |
+
"node_modules/@esbuild/openbsd-arm64": {
|
| 323 |
+
"version": "0.25.8",
|
| 324 |
+
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz",
|
| 325 |
+
"integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==",
|
| 326 |
+
"cpu": [
|
| 327 |
+
"arm64"
|
| 328 |
+
],
|
| 329 |
+
"license": "MIT",
|
| 330 |
+
"optional": true,
|
| 331 |
+
"os": [
|
| 332 |
+
"openbsd"
|
| 333 |
+
],
|
| 334 |
+
"engines": {
|
| 335 |
+
"node": ">=18"
|
| 336 |
+
}
|
| 337 |
+
},
|
| 338 |
+
"node_modules/@esbuild/openbsd-x64": {
|
| 339 |
+
"version": "0.25.8",
|
| 340 |
+
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz",
|
| 341 |
+
"integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==",
|
| 342 |
+
"cpu": [
|
| 343 |
+
"x64"
|
| 344 |
+
],
|
| 345 |
+
"license": "MIT",
|
| 346 |
+
"optional": true,
|
| 347 |
+
"os": [
|
| 348 |
+
"openbsd"
|
| 349 |
+
],
|
| 350 |
+
"engines": {
|
| 351 |
+
"node": ">=18"
|
| 352 |
+
}
|
| 353 |
+
},
|
| 354 |
+
"node_modules/@esbuild/openharmony-arm64": {
|
| 355 |
+
"version": "0.25.8",
|
| 356 |
+
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz",
|
| 357 |
+
"integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==",
|
| 358 |
+
"cpu": [
|
| 359 |
+
"arm64"
|
| 360 |
+
],
|
| 361 |
+
"license": "MIT",
|
| 362 |
+
"optional": true,
|
| 363 |
+
"os": [
|
| 364 |
+
"openharmony"
|
| 365 |
+
],
|
| 366 |
+
"engines": {
|
| 367 |
+
"node": ">=18"
|
| 368 |
+
}
|
| 369 |
+
},
|
| 370 |
+
"node_modules/@esbuild/sunos-x64": {
|
| 371 |
+
"version": "0.25.8",
|
| 372 |
+
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz",
|
| 373 |
+
"integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==",
|
| 374 |
+
"cpu": [
|
| 375 |
+
"x64"
|
| 376 |
+
],
|
| 377 |
+
"license": "MIT",
|
| 378 |
+
"optional": true,
|
| 379 |
+
"os": [
|
| 380 |
+
"sunos"
|
| 381 |
+
],
|
| 382 |
+
"engines": {
|
| 383 |
+
"node": ">=18"
|
| 384 |
+
}
|
| 385 |
+
},
|
| 386 |
+
"node_modules/@esbuild/win32-arm64": {
|
| 387 |
+
"version": "0.25.8",
|
| 388 |
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz",
|
| 389 |
+
"integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==",
|
| 390 |
+
"cpu": [
|
| 391 |
+
"arm64"
|
| 392 |
+
],
|
| 393 |
+
"license": "MIT",
|
| 394 |
+
"optional": true,
|
| 395 |
+
"os": [
|
| 396 |
+
"win32"
|
| 397 |
+
],
|
| 398 |
+
"engines": {
|
| 399 |
+
"node": ">=18"
|
| 400 |
+
}
|
| 401 |
+
},
|
| 402 |
+
"node_modules/@esbuild/win32-ia32": {
|
| 403 |
+
"version": "0.25.8",
|
| 404 |
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz",
|
| 405 |
+
"integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==",
|
| 406 |
+
"cpu": [
|
| 407 |
+
"ia32"
|
| 408 |
+
],
|
| 409 |
+
"license": "MIT",
|
| 410 |
+
"optional": true,
|
| 411 |
+
"os": [
|
| 412 |
+
"win32"
|
| 413 |
+
],
|
| 414 |
+
"engines": {
|
| 415 |
+
"node": ">=18"
|
| 416 |
+
}
|
| 417 |
+
},
|
| 418 |
+
"node_modules/@esbuild/win32-x64": {
|
| 419 |
+
"version": "0.25.8",
|
| 420 |
+
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz",
|
| 421 |
+
"integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==",
|
| 422 |
+
"cpu": [
|
| 423 |
+
"x64"
|
| 424 |
+
],
|
| 425 |
+
"license": "MIT",
|
| 426 |
+
"optional": true,
|
| 427 |
+
"os": [
|
| 428 |
+
"win32"
|
| 429 |
+
],
|
| 430 |
+
"engines": {
|
| 431 |
+
"node": ">=18"
|
| 432 |
+
}
|
| 433 |
+
},
|
| 434 |
+
"node_modules/@modelcontextprotocol/sdk": {
|
| 435 |
+
"version": "1.17.0",
|
| 436 |
+
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.17.0.tgz",
|
| 437 |
+
"integrity": "sha512-qFfbWFA7r1Sd8D697L7GkTd36yqDuTkvz0KfOGkgXR8EUhQn3/EDNIR/qUdQNMT8IjmasBvHWuXeisxtXTQT2g==",
|
| 438 |
+
"license": "MIT",
|
| 439 |
+
"optional": true,
|
| 440 |
+
"dependencies": {
|
| 441 |
+
"ajv": "^6.12.6",
|
| 442 |
+
"content-type": "^1.0.5",
|
| 443 |
+
"cors": "^2.8.5",
|
| 444 |
+
"cross-spawn": "^7.0.5",
|
| 445 |
+
"eventsource": "^3.0.2",
|
| 446 |
+
"eventsource-parser": "^3.0.0",
|
| 447 |
+
"express": "^5.0.1",
|
| 448 |
+
"express-rate-limit": "^7.5.0",
|
| 449 |
+
"pkce-challenge": "^5.0.0",
|
| 450 |
+
"raw-body": "^3.0.0",
|
| 451 |
+
"zod": "^3.23.8",
|
| 452 |
+
"zod-to-json-schema": "^3.24.1"
|
| 453 |
+
},
|
| 454 |
+
"engines": {
|
| 455 |
+
"node": ">=18"
|
| 456 |
+
}
|
| 457 |
+
},
|
| 458 |
+
"node_modules/@openai/agents": {
|
| 459 |
+
"version": "0.0.14",
|
| 460 |
+
"resolved": "https://registry.npmjs.org/@openai/agents/-/agents-0.0.14.tgz",
|
| 461 |
+
"integrity": "sha512-67FwkSxlid8/fFzIDMBuIvDQJ2Egf7PCpI7zp2JAlIlsz4UZVSlptNcN63RCG2xP6X2XqsdyjPke8ZDEKVrePw==",
|
| 462 |
+
"license": "MIT",
|
| 463 |
+
"dependencies": {
|
| 464 |
+
"@openai/agents-core": "0.0.14",
|
| 465 |
+
"@openai/agents-openai": "0.0.14",
|
| 466 |
+
"@openai/agents-realtime": "0.0.14",
|
| 467 |
+
"debug": "^4.4.0",
|
| 468 |
+
"openai": "^5.10.1"
|
| 469 |
+
}
|
| 470 |
+
},
|
| 471 |
+
"node_modules/@openai/agents-core": {
|
| 472 |
+
"version": "0.0.14",
|
| 473 |
+
"resolved": "https://registry.npmjs.org/@openai/agents-core/-/agents-core-0.0.14.tgz",
|
| 474 |
+
"integrity": "sha512-enCk5ucz+xxwPgh0zBQoJi5c1RukSc60neRUmlW4eQRgj9p5hVFQaBQNapZ4RysagHCLm2scYRwKgaP6nPDuNQ==",
|
| 475 |
+
"license": "MIT",
|
| 476 |
+
"dependencies": {
|
| 477 |
+
"@openai/zod": "npm:zod@3.25.40 - 3.25.67",
|
| 478 |
+
"debug": "^4.4.0",
|
| 479 |
+
"openai": "^5.10.1"
|
| 480 |
+
},
|
| 481 |
+
"optionalDependencies": {
|
| 482 |
+
"@modelcontextprotocol/sdk": "^1.12.0"
|
| 483 |
+
},
|
| 484 |
+
"peerDependencies": {
|
| 485 |
+
"zod": "3.25.40 - 3.25.67"
|
| 486 |
+
},
|
| 487 |
+
"peerDependenciesMeta": {
|
| 488 |
+
"zod": {
|
| 489 |
+
"optional": true
|
| 490 |
+
}
|
| 491 |
+
}
|
| 492 |
+
},
|
| 493 |
+
"node_modules/@openai/agents-openai": {
|
| 494 |
+
"version": "0.0.14",
|
| 495 |
+
"resolved": "https://registry.npmjs.org/@openai/agents-openai/-/agents-openai-0.0.14.tgz",
|
| 496 |
+
"integrity": "sha512-qSGBictwfJ3dMhC3QvqOLMm8RVZ/eIYNcFNLHps7hWeB1xeDGJFDZ/X7dDicejOeEXbi/nGe1ry6LbXDYSo3uQ==",
|
| 497 |
+
"license": "MIT",
|
| 498 |
+
"dependencies": {
|
| 499 |
+
"@openai/agents-core": "0.0.14",
|
| 500 |
+
"@openai/zod": "npm:zod@3.25.40 - 3.25.67",
|
| 501 |
+
"debug": "^4.4.0",
|
| 502 |
+
"openai": "^5.10.1"
|
| 503 |
+
}
|
| 504 |
+
},
|
| 505 |
+
"node_modules/@openai/agents-realtime": {
|
| 506 |
+
"version": "0.0.14",
|
| 507 |
+
"resolved": "https://registry.npmjs.org/@openai/agents-realtime/-/agents-realtime-0.0.14.tgz",
|
| 508 |
+
"integrity": "sha512-gfSuWEDKZREWi0DJDf3F8fT/xvLL9R0cydfgriL0kPkWOlTMuZ0KZKI6D90pc2VAWIescA8BuqCcWkgWFq55Uw==",
|
| 509 |
+
"license": "MIT",
|
| 510 |
+
"dependencies": {
|
| 511 |
+
"@openai/agents-core": "0.0.14",
|
| 512 |
+
"@openai/zod": "npm:zod@3.25.40 - 3.25.67",
|
| 513 |
+
"@types/ws": "^8.18.1",
|
| 514 |
+
"debug": "^4.4.0",
|
| 515 |
+
"ws": "^8.18.1"
|
| 516 |
+
}
|
| 517 |
+
},
|
| 518 |
+
"node_modules/@openai/zod": {
|
| 519 |
+
"name": "zod",
|
| 520 |
+
"version": "3.25.67",
|
| 521 |
+
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz",
|
| 522 |
+
"integrity": "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==",
|
| 523 |
+
"license": "MIT",
|
| 524 |
+
"funding": {
|
| 525 |
+
"url": "https://github.com/sponsors/colinhacks"
|
| 526 |
+
}
|
| 527 |
+
},
|
| 528 |
+
"node_modules/@types/node": {
|
| 529 |
+
"version": "24.1.0",
|
| 530 |
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.1.0.tgz",
|
| 531 |
+
"integrity": "sha512-ut5FthK5moxFKH2T1CUOC6ctR67rQRvvHdFLCD2Ql6KXmMuCrjsSsRI9UsLCm9M18BMwClv4pn327UvB7eeO1w==",
|
| 532 |
+
"license": "MIT",
|
| 533 |
+
"dependencies": {
|
| 534 |
+
"undici-types": "~7.8.0"
|
| 535 |
+
}
|
| 536 |
+
},
|
| 537 |
+
"node_modules/@types/ws": {
|
| 538 |
+
"version": "8.18.1",
|
| 539 |
+
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz",
|
| 540 |
+
"integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==",
|
| 541 |
+
"license": "MIT",
|
| 542 |
+
"dependencies": {
|
| 543 |
+
"@types/node": "*"
|
| 544 |
+
}
|
| 545 |
+
},
|
| 546 |
+
"node_modules/accepts": {
|
| 547 |
+
"version": "2.0.0",
|
| 548 |
+
"resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
|
| 549 |
+
"integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
|
| 550 |
+
"license": "MIT",
|
| 551 |
+
"optional": true,
|
| 552 |
+
"dependencies": {
|
| 553 |
+
"mime-types": "^3.0.0",
|
| 554 |
+
"negotiator": "^1.0.0"
|
| 555 |
+
},
|
| 556 |
+
"engines": {
|
| 557 |
+
"node": ">= 0.6"
|
| 558 |
+
}
|
| 559 |
+
},
|
| 560 |
+
"node_modules/ajv": {
|
| 561 |
+
"version": "6.12.6",
|
| 562 |
+
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
| 563 |
+
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
| 564 |
+
"license": "MIT",
|
| 565 |
+
"optional": true,
|
| 566 |
+
"dependencies": {
|
| 567 |
+
"fast-deep-equal": "^3.1.1",
|
| 568 |
+
"fast-json-stable-stringify": "^2.0.0",
|
| 569 |
+
"json-schema-traverse": "^0.4.1",
|
| 570 |
+
"uri-js": "^4.2.2"
|
| 571 |
+
},
|
| 572 |
+
"funding": {
|
| 573 |
+
"type": "github",
|
| 574 |
+
"url": "https://github.com/sponsors/epoberezkin"
|
| 575 |
+
}
|
| 576 |
+
},
|
| 577 |
+
"node_modules/body-parser": {
|
| 578 |
+
"version": "2.2.0",
|
| 579 |
+
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
|
| 580 |
+
"integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
|
| 581 |
+
"license": "MIT",
|
| 582 |
+
"optional": true,
|
| 583 |
+
"dependencies": {
|
| 584 |
+
"bytes": "^3.1.2",
|
| 585 |
+
"content-type": "^1.0.5",
|
| 586 |
+
"debug": "^4.4.0",
|
| 587 |
+
"http-errors": "^2.0.0",
|
| 588 |
+
"iconv-lite": "^0.6.3",
|
| 589 |
+
"on-finished": "^2.4.1",
|
| 590 |
+
"qs": "^6.14.0",
|
| 591 |
+
"raw-body": "^3.0.0",
|
| 592 |
+
"type-is": "^2.0.0"
|
| 593 |
+
},
|
| 594 |
+
"engines": {
|
| 595 |
+
"node": ">=18"
|
| 596 |
+
}
|
| 597 |
+
},
|
| 598 |
+
"node_modules/bytes": {
|
| 599 |
+
"version": "3.1.2",
|
| 600 |
+
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
| 601 |
+
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
| 602 |
+
"license": "MIT",
|
| 603 |
+
"optional": true,
|
| 604 |
+
"engines": {
|
| 605 |
+
"node": ">= 0.8"
|
| 606 |
+
}
|
| 607 |
+
},
|
| 608 |
+
"node_modules/call-bind-apply-helpers": {
|
| 609 |
+
"version": "1.0.2",
|
| 610 |
+
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
|
| 611 |
+
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
|
| 612 |
+
"license": "MIT",
|
| 613 |
+
"optional": true,
|
| 614 |
+
"dependencies": {
|
| 615 |
+
"es-errors": "^1.3.0",
|
| 616 |
+
"function-bind": "^1.1.2"
|
| 617 |
+
},
|
| 618 |
+
"engines": {
|
| 619 |
+
"node": ">= 0.4"
|
| 620 |
+
}
|
| 621 |
+
},
|
| 622 |
+
"node_modules/call-bound": {
|
| 623 |
+
"version": "1.0.4",
|
| 624 |
+
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
|
| 625 |
+
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
|
| 626 |
+
"license": "MIT",
|
| 627 |
+
"optional": true,
|
| 628 |
+
"dependencies": {
|
| 629 |
+
"call-bind-apply-helpers": "^1.0.2",
|
| 630 |
+
"get-intrinsic": "^1.3.0"
|
| 631 |
+
},
|
| 632 |
+
"engines": {
|
| 633 |
+
"node": ">= 0.4"
|
| 634 |
+
},
|
| 635 |
+
"funding": {
|
| 636 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 637 |
+
}
|
| 638 |
+
},
|
| 639 |
+
"node_modules/content-disposition": {
|
| 640 |
+
"version": "1.0.0",
|
| 641 |
+
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
|
| 642 |
+
"integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==",
|
| 643 |
+
"license": "MIT",
|
| 644 |
+
"optional": true,
|
| 645 |
+
"dependencies": {
|
| 646 |
+
"safe-buffer": "5.2.1"
|
| 647 |
+
},
|
| 648 |
+
"engines": {
|
| 649 |
+
"node": ">= 0.6"
|
| 650 |
+
}
|
| 651 |
+
},
|
| 652 |
+
"node_modules/content-type": {
|
| 653 |
+
"version": "1.0.5",
|
| 654 |
+
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
| 655 |
+
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
| 656 |
+
"license": "MIT",
|
| 657 |
+
"optional": true,
|
| 658 |
+
"engines": {
|
| 659 |
+
"node": ">= 0.6"
|
| 660 |
+
}
|
| 661 |
+
},
|
| 662 |
+
"node_modules/cookie": {
|
| 663 |
+
"version": "0.7.2",
|
| 664 |
+
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
|
| 665 |
+
"integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
|
| 666 |
+
"license": "MIT",
|
| 667 |
+
"optional": true,
|
| 668 |
+
"engines": {
|
| 669 |
+
"node": ">= 0.6"
|
| 670 |
+
}
|
| 671 |
+
},
|
| 672 |
+
"node_modules/cookie-signature": {
|
| 673 |
+
"version": "1.2.2",
|
| 674 |
+
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
|
| 675 |
+
"integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
|
| 676 |
+
"license": "MIT",
|
| 677 |
+
"optional": true,
|
| 678 |
+
"engines": {
|
| 679 |
+
"node": ">=6.6.0"
|
| 680 |
+
}
|
| 681 |
+
},
|
| 682 |
+
"node_modules/cors": {
|
| 683 |
+
"version": "2.8.5",
|
| 684 |
+
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
| 685 |
+
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
|
| 686 |
+
"license": "MIT",
|
| 687 |
+
"optional": true,
|
| 688 |
+
"dependencies": {
|
| 689 |
+
"object-assign": "^4",
|
| 690 |
+
"vary": "^1"
|
| 691 |
+
},
|
| 692 |
+
"engines": {
|
| 693 |
+
"node": ">= 0.10"
|
| 694 |
+
}
|
| 695 |
+
},
|
| 696 |
+
"node_modules/cross-spawn": {
|
| 697 |
+
"version": "7.0.6",
|
| 698 |
+
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
| 699 |
+
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
| 700 |
+
"license": "MIT",
|
| 701 |
+
"optional": true,
|
| 702 |
+
"dependencies": {
|
| 703 |
+
"path-key": "^3.1.0",
|
| 704 |
+
"shebang-command": "^2.0.0",
|
| 705 |
+
"which": "^2.0.1"
|
| 706 |
+
},
|
| 707 |
+
"engines": {
|
| 708 |
+
"node": ">= 8"
|
| 709 |
+
}
|
| 710 |
+
},
|
| 711 |
+
"node_modules/debug": {
|
| 712 |
+
"version": "4.4.1",
|
| 713 |
+
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
| 714 |
+
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
|
| 715 |
+
"license": "MIT",
|
| 716 |
+
"dependencies": {
|
| 717 |
+
"ms": "^2.1.3"
|
| 718 |
+
},
|
| 719 |
+
"engines": {
|
| 720 |
+
"node": ">=6.0"
|
| 721 |
+
},
|
| 722 |
+
"peerDependenciesMeta": {
|
| 723 |
+
"supports-color": {
|
| 724 |
+
"optional": true
|
| 725 |
+
}
|
| 726 |
+
}
|
| 727 |
+
},
|
| 728 |
+
"node_modules/depd": {
|
| 729 |
+
"version": "2.0.0",
|
| 730 |
+
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
| 731 |
+
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
|
| 732 |
+
"license": "MIT",
|
| 733 |
+
"optional": true,
|
| 734 |
+
"engines": {
|
| 735 |
+
"node": ">= 0.8"
|
| 736 |
+
}
|
| 737 |
+
},
|
| 738 |
+
"node_modules/dunder-proto": {
|
| 739 |
+
"version": "1.0.1",
|
| 740 |
+
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
| 741 |
+
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
|
| 742 |
+
"license": "MIT",
|
| 743 |
+
"optional": true,
|
| 744 |
+
"dependencies": {
|
| 745 |
+
"call-bind-apply-helpers": "^1.0.1",
|
| 746 |
+
"es-errors": "^1.3.0",
|
| 747 |
+
"gopd": "^1.2.0"
|
| 748 |
+
},
|
| 749 |
+
"engines": {
|
| 750 |
+
"node": ">= 0.4"
|
| 751 |
+
}
|
| 752 |
+
},
|
| 753 |
+
"node_modules/ee-first": {
|
| 754 |
+
"version": "1.1.1",
|
| 755 |
+
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
| 756 |
+
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
|
| 757 |
+
"license": "MIT",
|
| 758 |
+
"optional": true
|
| 759 |
+
},
|
| 760 |
+
"node_modules/encodeurl": {
|
| 761 |
+
"version": "2.0.0",
|
| 762 |
+
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
| 763 |
+
"integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
|
| 764 |
+
"license": "MIT",
|
| 765 |
+
"optional": true,
|
| 766 |
+
"engines": {
|
| 767 |
+
"node": ">= 0.8"
|
| 768 |
+
}
|
| 769 |
+
},
|
| 770 |
+
"node_modules/es-define-property": {
|
| 771 |
+
"version": "1.0.1",
|
| 772 |
+
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
| 773 |
+
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
|
| 774 |
+
"license": "MIT",
|
| 775 |
+
"optional": true,
|
| 776 |
+
"engines": {
|
| 777 |
+
"node": ">= 0.4"
|
| 778 |
+
}
|
| 779 |
+
},
|
| 780 |
+
"node_modules/es-errors": {
|
| 781 |
+
"version": "1.3.0",
|
| 782 |
+
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
| 783 |
+
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
| 784 |
+
"license": "MIT",
|
| 785 |
+
"optional": true,
|
| 786 |
+
"engines": {
|
| 787 |
+
"node": ">= 0.4"
|
| 788 |
+
}
|
| 789 |
+
},
|
| 790 |
+
"node_modules/es-object-atoms": {
|
| 791 |
+
"version": "1.1.1",
|
| 792 |
+
"resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
|
| 793 |
+
"integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
|
| 794 |
+
"license": "MIT",
|
| 795 |
+
"optional": true,
|
| 796 |
+
"dependencies": {
|
| 797 |
+
"es-errors": "^1.3.0"
|
| 798 |
+
},
|
| 799 |
+
"engines": {
|
| 800 |
+
"node": ">= 0.4"
|
| 801 |
+
}
|
| 802 |
+
},
|
| 803 |
+
"node_modules/esbuild": {
|
| 804 |
+
"version": "0.25.8",
|
| 805 |
+
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz",
|
| 806 |
+
"integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==",
|
| 807 |
+
"hasInstallScript": true,
|
| 808 |
+
"license": "MIT",
|
| 809 |
+
"bin": {
|
| 810 |
+
"esbuild": "bin/esbuild"
|
| 811 |
+
},
|
| 812 |
+
"engines": {
|
| 813 |
+
"node": ">=18"
|
| 814 |
+
},
|
| 815 |
+
"optionalDependencies": {
|
| 816 |
+
"@esbuild/aix-ppc64": "0.25.8",
|
| 817 |
+
"@esbuild/android-arm": "0.25.8",
|
| 818 |
+
"@esbuild/android-arm64": "0.25.8",
|
| 819 |
+
"@esbuild/android-x64": "0.25.8",
|
| 820 |
+
"@esbuild/darwin-arm64": "0.25.8",
|
| 821 |
+
"@esbuild/darwin-x64": "0.25.8",
|
| 822 |
+
"@esbuild/freebsd-arm64": "0.25.8",
|
| 823 |
+
"@esbuild/freebsd-x64": "0.25.8",
|
| 824 |
+
"@esbuild/linux-arm": "0.25.8",
|
| 825 |
+
"@esbuild/linux-arm64": "0.25.8",
|
| 826 |
+
"@esbuild/linux-ia32": "0.25.8",
|
| 827 |
+
"@esbuild/linux-loong64": "0.25.8",
|
| 828 |
+
"@esbuild/linux-mips64el": "0.25.8",
|
| 829 |
+
"@esbuild/linux-ppc64": "0.25.8",
|
| 830 |
+
"@esbuild/linux-riscv64": "0.25.8",
|
| 831 |
+
"@esbuild/linux-s390x": "0.25.8",
|
| 832 |
+
"@esbuild/linux-x64": "0.25.8",
|
| 833 |
+
"@esbuild/netbsd-arm64": "0.25.8",
|
| 834 |
+
"@esbuild/netbsd-x64": "0.25.8",
|
| 835 |
+
"@esbuild/openbsd-arm64": "0.25.8",
|
| 836 |
+
"@esbuild/openbsd-x64": "0.25.8",
|
| 837 |
+
"@esbuild/openharmony-arm64": "0.25.8",
|
| 838 |
+
"@esbuild/sunos-x64": "0.25.8",
|
| 839 |
+
"@esbuild/win32-arm64": "0.25.8",
|
| 840 |
+
"@esbuild/win32-ia32": "0.25.8",
|
| 841 |
+
"@esbuild/win32-x64": "0.25.8"
|
| 842 |
+
}
|
| 843 |
+
},
|
| 844 |
+
"node_modules/escape-html": {
|
| 845 |
+
"version": "1.0.3",
|
| 846 |
+
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
| 847 |
+
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
|
| 848 |
+
"license": "MIT",
|
| 849 |
+
"optional": true
|
| 850 |
+
},
|
| 851 |
+
"node_modules/etag": {
|
| 852 |
+
"version": "1.8.1",
|
| 853 |
+
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
| 854 |
+
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
|
| 855 |
+
"license": "MIT",
|
| 856 |
+
"optional": true,
|
| 857 |
+
"engines": {
|
| 858 |
+
"node": ">= 0.6"
|
| 859 |
+
}
|
| 860 |
+
},
|
| 861 |
+
"node_modules/eventsource": {
|
| 862 |
+
"version": "3.0.7",
|
| 863 |
+
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz",
|
| 864 |
+
"integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==",
|
| 865 |
+
"license": "MIT",
|
| 866 |
+
"optional": true,
|
| 867 |
+
"dependencies": {
|
| 868 |
+
"eventsource-parser": "^3.0.1"
|
| 869 |
+
},
|
| 870 |
+
"engines": {
|
| 871 |
+
"node": ">=18.0.0"
|
| 872 |
+
}
|
| 873 |
+
},
|
| 874 |
+
"node_modules/eventsource-parser": {
|
| 875 |
+
"version": "3.0.3",
|
| 876 |
+
"resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.3.tgz",
|
| 877 |
+
"integrity": "sha512-nVpZkTMM9rF6AQ9gPJpFsNAMt48wIzB5TQgiTLdHiuO8XEDhUgZEhqKlZWXbIzo9VmJ/HvysHqEaVeD5v9TPvA==",
|
| 878 |
+
"license": "MIT",
|
| 879 |
+
"optional": true,
|
| 880 |
+
"engines": {
|
| 881 |
+
"node": ">=20.0.0"
|
| 882 |
+
}
|
| 883 |
+
},
|
| 884 |
+
"node_modules/express": {
|
| 885 |
+
"version": "5.1.0",
|
| 886 |
+
"resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
|
| 887 |
+
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
|
| 888 |
+
"license": "MIT",
|
| 889 |
+
"optional": true,
|
| 890 |
+
"dependencies": {
|
| 891 |
+
"accepts": "^2.0.0",
|
| 892 |
+
"body-parser": "^2.2.0",
|
| 893 |
+
"content-disposition": "^1.0.0",
|
| 894 |
+
"content-type": "^1.0.5",
|
| 895 |
+
"cookie": "^0.7.1",
|
| 896 |
+
"cookie-signature": "^1.2.1",
|
| 897 |
+
"debug": "^4.4.0",
|
| 898 |
+
"encodeurl": "^2.0.0",
|
| 899 |
+
"escape-html": "^1.0.3",
|
| 900 |
+
"etag": "^1.8.1",
|
| 901 |
+
"finalhandler": "^2.1.0",
|
| 902 |
+
"fresh": "^2.0.0",
|
| 903 |
+
"http-errors": "^2.0.0",
|
| 904 |
+
"merge-descriptors": "^2.0.0",
|
| 905 |
+
"mime-types": "^3.0.0",
|
| 906 |
+
"on-finished": "^2.4.1",
|
| 907 |
+
"once": "^1.4.0",
|
| 908 |
+
"parseurl": "^1.3.3",
|
| 909 |
+
"proxy-addr": "^2.0.7",
|
| 910 |
+
"qs": "^6.14.0",
|
| 911 |
+
"range-parser": "^1.2.1",
|
| 912 |
+
"router": "^2.2.0",
|
| 913 |
+
"send": "^1.1.0",
|
| 914 |
+
"serve-static": "^2.2.0",
|
| 915 |
+
"statuses": "^2.0.1",
|
| 916 |
+
"type-is": "^2.0.1",
|
| 917 |
+
"vary": "^1.1.2"
|
| 918 |
+
},
|
| 919 |
+
"engines": {
|
| 920 |
+
"node": ">= 18"
|
| 921 |
+
},
|
| 922 |
+
"funding": {
|
| 923 |
+
"type": "opencollective",
|
| 924 |
+
"url": "https://opencollective.com/express"
|
| 925 |
+
}
|
| 926 |
+
},
|
| 927 |
+
"node_modules/express-rate-limit": {
|
| 928 |
+
"version": "7.5.1",
|
| 929 |
+
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz",
|
| 930 |
+
"integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==",
|
| 931 |
+
"license": "MIT",
|
| 932 |
+
"optional": true,
|
| 933 |
+
"engines": {
|
| 934 |
+
"node": ">= 16"
|
| 935 |
+
},
|
| 936 |
+
"funding": {
|
| 937 |
+
"url": "https://github.com/sponsors/express-rate-limit"
|
| 938 |
+
},
|
| 939 |
+
"peerDependencies": {
|
| 940 |
+
"express": ">= 4.11"
|
| 941 |
+
}
|
| 942 |
+
},
|
| 943 |
+
"node_modules/fast-deep-equal": {
|
| 944 |
+
"version": "3.1.3",
|
| 945 |
+
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
| 946 |
+
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
| 947 |
+
"license": "MIT",
|
| 948 |
+
"optional": true
|
| 949 |
+
},
|
| 950 |
+
"node_modules/fast-json-stable-stringify": {
|
| 951 |
+
"version": "2.1.0",
|
| 952 |
+
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
| 953 |
+
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
|
| 954 |
+
"license": "MIT",
|
| 955 |
+
"optional": true
|
| 956 |
+
},
|
| 957 |
+
"node_modules/finalhandler": {
|
| 958 |
+
"version": "2.1.0",
|
| 959 |
+
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
|
| 960 |
+
"integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==",
|
| 961 |
+
"license": "MIT",
|
| 962 |
+
"optional": true,
|
| 963 |
+
"dependencies": {
|
| 964 |
+
"debug": "^4.4.0",
|
| 965 |
+
"encodeurl": "^2.0.0",
|
| 966 |
+
"escape-html": "^1.0.3",
|
| 967 |
+
"on-finished": "^2.4.1",
|
| 968 |
+
"parseurl": "^1.3.3",
|
| 969 |
+
"statuses": "^2.0.1"
|
| 970 |
+
},
|
| 971 |
+
"engines": {
|
| 972 |
+
"node": ">= 0.8"
|
| 973 |
+
}
|
| 974 |
+
},
|
| 975 |
+
"node_modules/forwarded": {
|
| 976 |
+
"version": "0.2.0",
|
| 977 |
+
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
| 978 |
+
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
|
| 979 |
+
"license": "MIT",
|
| 980 |
+
"optional": true,
|
| 981 |
+
"engines": {
|
| 982 |
+
"node": ">= 0.6"
|
| 983 |
+
}
|
| 984 |
+
},
|
| 985 |
+
"node_modules/fresh": {
|
| 986 |
+
"version": "2.0.0",
|
| 987 |
+
"resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
|
| 988 |
+
"integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
|
| 989 |
+
"license": "MIT",
|
| 990 |
+
"optional": true,
|
| 991 |
+
"engines": {
|
| 992 |
+
"node": ">= 0.8"
|
| 993 |
+
}
|
| 994 |
+
},
|
| 995 |
+
"node_modules/fsevents": {
|
| 996 |
+
"version": "2.3.3",
|
| 997 |
+
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
| 998 |
+
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
| 999 |
+
"hasInstallScript": true,
|
| 1000 |
+
"license": "MIT",
|
| 1001 |
+
"optional": true,
|
| 1002 |
+
"os": [
|
| 1003 |
+
"darwin"
|
| 1004 |
+
],
|
| 1005 |
+
"engines": {
|
| 1006 |
+
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
| 1007 |
+
}
|
| 1008 |
+
},
|
| 1009 |
+
"node_modules/function-bind": {
|
| 1010 |
+
"version": "1.1.2",
|
| 1011 |
+
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
| 1012 |
+
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
| 1013 |
+
"license": "MIT",
|
| 1014 |
+
"optional": true,
|
| 1015 |
+
"funding": {
|
| 1016 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 1017 |
+
}
|
| 1018 |
+
},
|
| 1019 |
+
"node_modules/get-intrinsic": {
|
| 1020 |
+
"version": "1.3.0",
|
| 1021 |
+
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
| 1022 |
+
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
|
| 1023 |
+
"license": "MIT",
|
| 1024 |
+
"optional": true,
|
| 1025 |
+
"dependencies": {
|
| 1026 |
+
"call-bind-apply-helpers": "^1.0.2",
|
| 1027 |
+
"es-define-property": "^1.0.1",
|
| 1028 |
+
"es-errors": "^1.3.0",
|
| 1029 |
+
"es-object-atoms": "^1.1.1",
|
| 1030 |
+
"function-bind": "^1.1.2",
|
| 1031 |
+
"get-proto": "^1.0.1",
|
| 1032 |
+
"gopd": "^1.2.0",
|
| 1033 |
+
"has-symbols": "^1.1.0",
|
| 1034 |
+
"hasown": "^2.0.2",
|
| 1035 |
+
"math-intrinsics": "^1.1.0"
|
| 1036 |
+
},
|
| 1037 |
+
"engines": {
|
| 1038 |
+
"node": ">= 0.4"
|
| 1039 |
+
},
|
| 1040 |
+
"funding": {
|
| 1041 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 1042 |
+
}
|
| 1043 |
+
},
|
| 1044 |
+
"node_modules/get-proto": {
|
| 1045 |
+
"version": "1.0.1",
|
| 1046 |
+
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
| 1047 |
+
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
|
| 1048 |
+
"license": "MIT",
|
| 1049 |
+
"optional": true,
|
| 1050 |
+
"dependencies": {
|
| 1051 |
+
"dunder-proto": "^1.0.1",
|
| 1052 |
+
"es-object-atoms": "^1.0.0"
|
| 1053 |
+
},
|
| 1054 |
+
"engines": {
|
| 1055 |
+
"node": ">= 0.4"
|
| 1056 |
+
}
|
| 1057 |
+
},
|
| 1058 |
+
"node_modules/get-tsconfig": {
|
| 1059 |
+
"version": "4.10.1",
|
| 1060 |
+
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz",
|
| 1061 |
+
"integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==",
|
| 1062 |
+
"license": "MIT",
|
| 1063 |
+
"dependencies": {
|
| 1064 |
+
"resolve-pkg-maps": "^1.0.0"
|
| 1065 |
+
},
|
| 1066 |
+
"funding": {
|
| 1067 |
+
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
|
| 1068 |
+
}
|
| 1069 |
+
},
|
| 1070 |
+
"node_modules/gopd": {
|
| 1071 |
+
"version": "1.2.0",
|
| 1072 |
+
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
|
| 1073 |
+
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
|
| 1074 |
+
"license": "MIT",
|
| 1075 |
+
"optional": true,
|
| 1076 |
+
"engines": {
|
| 1077 |
+
"node": ">= 0.4"
|
| 1078 |
+
},
|
| 1079 |
+
"funding": {
|
| 1080 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 1081 |
+
}
|
| 1082 |
+
},
|
| 1083 |
+
"node_modules/has-symbols": {
|
| 1084 |
+
"version": "1.1.0",
|
| 1085 |
+
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
| 1086 |
+
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
|
| 1087 |
+
"license": "MIT",
|
| 1088 |
+
"optional": true,
|
| 1089 |
+
"engines": {
|
| 1090 |
+
"node": ">= 0.4"
|
| 1091 |
+
},
|
| 1092 |
+
"funding": {
|
| 1093 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 1094 |
+
}
|
| 1095 |
+
},
|
| 1096 |
+
"node_modules/hasown": {
|
| 1097 |
+
"version": "2.0.2",
|
| 1098 |
+
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
| 1099 |
+
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
| 1100 |
+
"license": "MIT",
|
| 1101 |
+
"optional": true,
|
| 1102 |
+
"dependencies": {
|
| 1103 |
+
"function-bind": "^1.1.2"
|
| 1104 |
+
},
|
| 1105 |
+
"engines": {
|
| 1106 |
+
"node": ">= 0.4"
|
| 1107 |
+
}
|
| 1108 |
+
},
|
| 1109 |
+
"node_modules/http-errors": {
|
| 1110 |
+
"version": "2.0.0",
|
| 1111 |
+
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
| 1112 |
+
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
|
| 1113 |
+
"license": "MIT",
|
| 1114 |
+
"optional": true,
|
| 1115 |
+
"dependencies": {
|
| 1116 |
+
"depd": "2.0.0",
|
| 1117 |
+
"inherits": "2.0.4",
|
| 1118 |
+
"setprototypeof": "1.2.0",
|
| 1119 |
+
"statuses": "2.0.1",
|
| 1120 |
+
"toidentifier": "1.0.1"
|
| 1121 |
+
},
|
| 1122 |
+
"engines": {
|
| 1123 |
+
"node": ">= 0.8"
|
| 1124 |
+
}
|
| 1125 |
+
},
|
| 1126 |
+
"node_modules/http-errors/node_modules/statuses": {
|
| 1127 |
+
"version": "2.0.1",
|
| 1128 |
+
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
| 1129 |
+
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
|
| 1130 |
+
"license": "MIT",
|
| 1131 |
+
"optional": true,
|
| 1132 |
+
"engines": {
|
| 1133 |
+
"node": ">= 0.8"
|
| 1134 |
+
}
|
| 1135 |
+
},
|
| 1136 |
+
"node_modules/iconv-lite": {
|
| 1137 |
+
"version": "0.6.3",
|
| 1138 |
+
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
| 1139 |
+
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
| 1140 |
+
"license": "MIT",
|
| 1141 |
+
"optional": true,
|
| 1142 |
+
"dependencies": {
|
| 1143 |
+
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
| 1144 |
+
},
|
| 1145 |
+
"engines": {
|
| 1146 |
+
"node": ">=0.10.0"
|
| 1147 |
+
}
|
| 1148 |
+
},
|
| 1149 |
+
"node_modules/inherits": {
|
| 1150 |
+
"version": "2.0.4",
|
| 1151 |
+
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
| 1152 |
+
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
| 1153 |
+
"license": "ISC",
|
| 1154 |
+
"optional": true
|
| 1155 |
+
},
|
| 1156 |
+
"node_modules/ipaddr.js": {
|
| 1157 |
+
"version": "1.9.1",
|
| 1158 |
+
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
| 1159 |
+
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
|
| 1160 |
+
"license": "MIT",
|
| 1161 |
+
"optional": true,
|
| 1162 |
+
"engines": {
|
| 1163 |
+
"node": ">= 0.10"
|
| 1164 |
+
}
|
| 1165 |
+
},
|
| 1166 |
+
"node_modules/is-promise": {
|
| 1167 |
+
"version": "4.0.0",
|
| 1168 |
+
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
|
| 1169 |
+
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
|
| 1170 |
+
"license": "MIT",
|
| 1171 |
+
"optional": true
|
| 1172 |
+
},
|
| 1173 |
+
"node_modules/isexe": {
|
| 1174 |
+
"version": "2.0.0",
|
| 1175 |
+
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
| 1176 |
+
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
| 1177 |
+
"license": "ISC",
|
| 1178 |
+
"optional": true
|
| 1179 |
+
},
|
| 1180 |
+
"node_modules/json-schema-traverse": {
|
| 1181 |
+
"version": "0.4.1",
|
| 1182 |
+
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
| 1183 |
+
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
| 1184 |
+
"license": "MIT",
|
| 1185 |
+
"optional": true
|
| 1186 |
+
},
|
| 1187 |
+
"node_modules/math-intrinsics": {
|
| 1188 |
+
"version": "1.1.0",
|
| 1189 |
+
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
| 1190 |
+
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
|
| 1191 |
+
"license": "MIT",
|
| 1192 |
+
"optional": true,
|
| 1193 |
+
"engines": {
|
| 1194 |
+
"node": ">= 0.4"
|
| 1195 |
+
}
|
| 1196 |
+
},
|
| 1197 |
+
"node_modules/media-typer": {
|
| 1198 |
+
"version": "1.1.0",
|
| 1199 |
+
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
|
| 1200 |
+
"integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
|
| 1201 |
+
"license": "MIT",
|
| 1202 |
+
"optional": true,
|
| 1203 |
+
"engines": {
|
| 1204 |
+
"node": ">= 0.8"
|
| 1205 |
+
}
|
| 1206 |
+
},
|
| 1207 |
+
"node_modules/merge-descriptors": {
|
| 1208 |
+
"version": "2.0.0",
|
| 1209 |
+
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
|
| 1210 |
+
"integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
|
| 1211 |
+
"license": "MIT",
|
| 1212 |
+
"optional": true,
|
| 1213 |
+
"engines": {
|
| 1214 |
+
"node": ">=18"
|
| 1215 |
+
},
|
| 1216 |
+
"funding": {
|
| 1217 |
+
"url": "https://github.com/sponsors/sindresorhus"
|
| 1218 |
+
}
|
| 1219 |
+
},
|
| 1220 |
+
"node_modules/mime-db": {
|
| 1221 |
+
"version": "1.54.0",
|
| 1222 |
+
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
| 1223 |
+
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
|
| 1224 |
+
"license": "MIT",
|
| 1225 |
+
"optional": true,
|
| 1226 |
+
"engines": {
|
| 1227 |
+
"node": ">= 0.6"
|
| 1228 |
+
}
|
| 1229 |
+
},
|
| 1230 |
+
"node_modules/mime-types": {
|
| 1231 |
+
"version": "3.0.1",
|
| 1232 |
+
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
|
| 1233 |
+
"integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
|
| 1234 |
+
"license": "MIT",
|
| 1235 |
+
"optional": true,
|
| 1236 |
+
"dependencies": {
|
| 1237 |
+
"mime-db": "^1.54.0"
|
| 1238 |
+
},
|
| 1239 |
+
"engines": {
|
| 1240 |
+
"node": ">= 0.6"
|
| 1241 |
+
}
|
| 1242 |
+
},
|
| 1243 |
+
"node_modules/ms": {
|
| 1244 |
+
"version": "2.1.3",
|
| 1245 |
+
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
| 1246 |
+
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
| 1247 |
+
"license": "MIT"
|
| 1248 |
+
},
|
| 1249 |
+
"node_modules/negotiator": {
|
| 1250 |
+
"version": "1.0.0",
|
| 1251 |
+
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
|
| 1252 |
+
"integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
|
| 1253 |
+
"license": "MIT",
|
| 1254 |
+
"optional": true,
|
| 1255 |
+
"engines": {
|
| 1256 |
+
"node": ">= 0.6"
|
| 1257 |
+
}
|
| 1258 |
+
},
|
| 1259 |
+
"node_modules/object-assign": {
|
| 1260 |
+
"version": "4.1.1",
|
| 1261 |
+
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
| 1262 |
+
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
| 1263 |
+
"license": "MIT",
|
| 1264 |
+
"optional": true,
|
| 1265 |
+
"engines": {
|
| 1266 |
+
"node": ">=0.10.0"
|
| 1267 |
+
}
|
| 1268 |
+
},
|
| 1269 |
+
"node_modules/object-inspect": {
|
| 1270 |
+
"version": "1.13.4",
|
| 1271 |
+
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
| 1272 |
+
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
|
| 1273 |
+
"license": "MIT",
|
| 1274 |
+
"optional": true,
|
| 1275 |
+
"engines": {
|
| 1276 |
+
"node": ">= 0.4"
|
| 1277 |
+
},
|
| 1278 |
+
"funding": {
|
| 1279 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 1280 |
+
}
|
| 1281 |
+
},
|
| 1282 |
+
"node_modules/on-finished": {
|
| 1283 |
+
"version": "2.4.1",
|
| 1284 |
+
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
| 1285 |
+
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
| 1286 |
+
"license": "MIT",
|
| 1287 |
+
"optional": true,
|
| 1288 |
+
"dependencies": {
|
| 1289 |
+
"ee-first": "1.1.1"
|
| 1290 |
+
},
|
| 1291 |
+
"engines": {
|
| 1292 |
+
"node": ">= 0.8"
|
| 1293 |
+
}
|
| 1294 |
+
},
|
| 1295 |
+
"node_modules/once": {
|
| 1296 |
+
"version": "1.4.0",
|
| 1297 |
+
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
| 1298 |
+
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
| 1299 |
+
"license": "ISC",
|
| 1300 |
+
"optional": true,
|
| 1301 |
+
"dependencies": {
|
| 1302 |
+
"wrappy": "1"
|
| 1303 |
+
}
|
| 1304 |
+
},
|
| 1305 |
+
"node_modules/openai": {
|
| 1306 |
+
"version": "5.11.0",
|
| 1307 |
+
"resolved": "https://registry.npmjs.org/openai/-/openai-5.11.0.tgz",
|
| 1308 |
+
"integrity": "sha512-+AuTc5pVjlnTuA9zvn8rA/k+1RluPIx9AD4eDcnutv6JNwHHZxIhkFy+tmMKCvmMFDQzfA/r1ujvPWB19DQkYg==",
|
| 1309 |
+
"license": "Apache-2.0",
|
| 1310 |
+
"bin": {
|
| 1311 |
+
"openai": "bin/cli"
|
| 1312 |
+
},
|
| 1313 |
+
"peerDependencies": {
|
| 1314 |
+
"ws": "^8.18.0",
|
| 1315 |
+
"zod": "^3.23.8"
|
| 1316 |
+
},
|
| 1317 |
+
"peerDependenciesMeta": {
|
| 1318 |
+
"ws": {
|
| 1319 |
+
"optional": true
|
| 1320 |
+
},
|
| 1321 |
+
"zod": {
|
| 1322 |
+
"optional": true
|
| 1323 |
+
}
|
| 1324 |
+
}
|
| 1325 |
+
},
|
| 1326 |
+
"node_modules/parseurl": {
|
| 1327 |
+
"version": "1.3.3",
|
| 1328 |
+
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
| 1329 |
+
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
|
| 1330 |
+
"license": "MIT",
|
| 1331 |
+
"optional": true,
|
| 1332 |
+
"engines": {
|
| 1333 |
+
"node": ">= 0.8"
|
| 1334 |
+
}
|
| 1335 |
+
},
|
| 1336 |
+
"node_modules/path-key": {
|
| 1337 |
+
"version": "3.1.1",
|
| 1338 |
+
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
| 1339 |
+
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
| 1340 |
+
"license": "MIT",
|
| 1341 |
+
"optional": true,
|
| 1342 |
+
"engines": {
|
| 1343 |
+
"node": ">=8"
|
| 1344 |
+
}
|
| 1345 |
+
},
|
| 1346 |
+
"node_modules/path-to-regexp": {
|
| 1347 |
+
"version": "8.2.0",
|
| 1348 |
+
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
|
| 1349 |
+
"integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==",
|
| 1350 |
+
"license": "MIT",
|
| 1351 |
+
"optional": true,
|
| 1352 |
+
"engines": {
|
| 1353 |
+
"node": ">=16"
|
| 1354 |
+
}
|
| 1355 |
+
},
|
| 1356 |
+
"node_modules/pkce-challenge": {
|
| 1357 |
+
"version": "5.0.0",
|
| 1358 |
+
"resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz",
|
| 1359 |
+
"integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==",
|
| 1360 |
+
"license": "MIT",
|
| 1361 |
+
"optional": true,
|
| 1362 |
+
"engines": {
|
| 1363 |
+
"node": ">=16.20.0"
|
| 1364 |
+
}
|
| 1365 |
+
},
|
| 1366 |
+
"node_modules/proxy-addr": {
|
| 1367 |
+
"version": "2.0.7",
|
| 1368 |
+
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
| 1369 |
+
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
|
| 1370 |
+
"license": "MIT",
|
| 1371 |
+
"optional": true,
|
| 1372 |
+
"dependencies": {
|
| 1373 |
+
"forwarded": "0.2.0",
|
| 1374 |
+
"ipaddr.js": "1.9.1"
|
| 1375 |
+
},
|
| 1376 |
+
"engines": {
|
| 1377 |
+
"node": ">= 0.10"
|
| 1378 |
+
}
|
| 1379 |
+
},
|
| 1380 |
+
"node_modules/punycode": {
|
| 1381 |
+
"version": "2.3.1",
|
| 1382 |
+
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
| 1383 |
+
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
| 1384 |
+
"license": "MIT",
|
| 1385 |
+
"optional": true,
|
| 1386 |
+
"engines": {
|
| 1387 |
+
"node": ">=6"
|
| 1388 |
+
}
|
| 1389 |
+
},
|
| 1390 |
+
"node_modules/qs": {
|
| 1391 |
+
"version": "6.14.0",
|
| 1392 |
+
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
|
| 1393 |
+
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
|
| 1394 |
+
"license": "BSD-3-Clause",
|
| 1395 |
+
"optional": true,
|
| 1396 |
+
"dependencies": {
|
| 1397 |
+
"side-channel": "^1.1.0"
|
| 1398 |
+
},
|
| 1399 |
+
"engines": {
|
| 1400 |
+
"node": ">=0.6"
|
| 1401 |
+
},
|
| 1402 |
+
"funding": {
|
| 1403 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 1404 |
+
}
|
| 1405 |
+
},
|
| 1406 |
+
"node_modules/range-parser": {
|
| 1407 |
+
"version": "1.2.1",
|
| 1408 |
+
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
| 1409 |
+
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
|
| 1410 |
+
"license": "MIT",
|
| 1411 |
+
"optional": true,
|
| 1412 |
+
"engines": {
|
| 1413 |
+
"node": ">= 0.6"
|
| 1414 |
+
}
|
| 1415 |
+
},
|
| 1416 |
+
"node_modules/raw-body": {
|
| 1417 |
+
"version": "3.0.0",
|
| 1418 |
+
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
|
| 1419 |
+
"integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
|
| 1420 |
+
"license": "MIT",
|
| 1421 |
+
"optional": true,
|
| 1422 |
+
"dependencies": {
|
| 1423 |
+
"bytes": "3.1.2",
|
| 1424 |
+
"http-errors": "2.0.0",
|
| 1425 |
+
"iconv-lite": "0.6.3",
|
| 1426 |
+
"unpipe": "1.0.0"
|
| 1427 |
+
},
|
| 1428 |
+
"engines": {
|
| 1429 |
+
"node": ">= 0.8"
|
| 1430 |
+
}
|
| 1431 |
+
},
|
| 1432 |
+
"node_modules/resolve-pkg-maps": {
|
| 1433 |
+
"version": "1.0.0",
|
| 1434 |
+
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
|
| 1435 |
+
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
|
| 1436 |
+
"license": "MIT",
|
| 1437 |
+
"funding": {
|
| 1438 |
+
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
|
| 1439 |
+
}
|
| 1440 |
+
},
|
| 1441 |
+
"node_modules/router": {
|
| 1442 |
+
"version": "2.2.0",
|
| 1443 |
+
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
|
| 1444 |
+
"integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
|
| 1445 |
+
"license": "MIT",
|
| 1446 |
+
"optional": true,
|
| 1447 |
+
"dependencies": {
|
| 1448 |
+
"debug": "^4.4.0",
|
| 1449 |
+
"depd": "^2.0.0",
|
| 1450 |
+
"is-promise": "^4.0.0",
|
| 1451 |
+
"parseurl": "^1.3.3",
|
| 1452 |
+
"path-to-regexp": "^8.0.0"
|
| 1453 |
+
},
|
| 1454 |
+
"engines": {
|
| 1455 |
+
"node": ">= 18"
|
| 1456 |
+
}
|
| 1457 |
+
},
|
| 1458 |
+
"node_modules/safe-buffer": {
|
| 1459 |
+
"version": "5.2.1",
|
| 1460 |
+
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
| 1461 |
+
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
| 1462 |
+
"funding": [
|
| 1463 |
+
{
|
| 1464 |
+
"type": "github",
|
| 1465 |
+
"url": "https://github.com/sponsors/feross"
|
| 1466 |
+
},
|
| 1467 |
+
{
|
| 1468 |
+
"type": "patreon",
|
| 1469 |
+
"url": "https://www.patreon.com/feross"
|
| 1470 |
+
},
|
| 1471 |
+
{
|
| 1472 |
+
"type": "consulting",
|
| 1473 |
+
"url": "https://feross.org/support"
|
| 1474 |
+
}
|
| 1475 |
+
],
|
| 1476 |
+
"license": "MIT",
|
| 1477 |
+
"optional": true
|
| 1478 |
+
},
|
| 1479 |
+
"node_modules/safer-buffer": {
|
| 1480 |
+
"version": "2.1.2",
|
| 1481 |
+
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
| 1482 |
+
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
| 1483 |
+
"license": "MIT",
|
| 1484 |
+
"optional": true
|
| 1485 |
+
},
|
| 1486 |
+
"node_modules/send": {
|
| 1487 |
+
"version": "1.2.0",
|
| 1488 |
+
"resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
|
| 1489 |
+
"integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
|
| 1490 |
+
"license": "MIT",
|
| 1491 |
+
"optional": true,
|
| 1492 |
+
"dependencies": {
|
| 1493 |
+
"debug": "^4.3.5",
|
| 1494 |
+
"encodeurl": "^2.0.0",
|
| 1495 |
+
"escape-html": "^1.0.3",
|
| 1496 |
+
"etag": "^1.8.1",
|
| 1497 |
+
"fresh": "^2.0.0",
|
| 1498 |
+
"http-errors": "^2.0.0",
|
| 1499 |
+
"mime-types": "^3.0.1",
|
| 1500 |
+
"ms": "^2.1.3",
|
| 1501 |
+
"on-finished": "^2.4.1",
|
| 1502 |
+
"range-parser": "^1.2.1",
|
| 1503 |
+
"statuses": "^2.0.1"
|
| 1504 |
+
},
|
| 1505 |
+
"engines": {
|
| 1506 |
+
"node": ">= 18"
|
| 1507 |
+
}
|
| 1508 |
+
},
|
| 1509 |
+
"node_modules/serve-static": {
|
| 1510 |
+
"version": "2.2.0",
|
| 1511 |
+
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz",
|
| 1512 |
+
"integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
|
| 1513 |
+
"license": "MIT",
|
| 1514 |
+
"optional": true,
|
| 1515 |
+
"dependencies": {
|
| 1516 |
+
"encodeurl": "^2.0.0",
|
| 1517 |
+
"escape-html": "^1.0.3",
|
| 1518 |
+
"parseurl": "^1.3.3",
|
| 1519 |
+
"send": "^1.2.0"
|
| 1520 |
+
},
|
| 1521 |
+
"engines": {
|
| 1522 |
+
"node": ">= 18"
|
| 1523 |
+
}
|
| 1524 |
+
},
|
| 1525 |
+
"node_modules/setprototypeof": {
|
| 1526 |
+
"version": "1.2.0",
|
| 1527 |
+
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
| 1528 |
+
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
|
| 1529 |
+
"license": "ISC",
|
| 1530 |
+
"optional": true
|
| 1531 |
+
},
|
| 1532 |
+
"node_modules/shebang-command": {
|
| 1533 |
+
"version": "2.0.0",
|
| 1534 |
+
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
| 1535 |
+
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
| 1536 |
+
"license": "MIT",
|
| 1537 |
+
"optional": true,
|
| 1538 |
+
"dependencies": {
|
| 1539 |
+
"shebang-regex": "^3.0.0"
|
| 1540 |
+
},
|
| 1541 |
+
"engines": {
|
| 1542 |
+
"node": ">=8"
|
| 1543 |
+
}
|
| 1544 |
+
},
|
| 1545 |
+
"node_modules/shebang-regex": {
|
| 1546 |
+
"version": "3.0.0",
|
| 1547 |
+
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
| 1548 |
+
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
| 1549 |
+
"license": "MIT",
|
| 1550 |
+
"optional": true,
|
| 1551 |
+
"engines": {
|
| 1552 |
+
"node": ">=8"
|
| 1553 |
+
}
|
| 1554 |
+
},
|
| 1555 |
+
"node_modules/side-channel": {
|
| 1556 |
+
"version": "1.1.0",
|
| 1557 |
+
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
| 1558 |
+
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
|
| 1559 |
+
"license": "MIT",
|
| 1560 |
+
"optional": true,
|
| 1561 |
+
"dependencies": {
|
| 1562 |
+
"es-errors": "^1.3.0",
|
| 1563 |
+
"object-inspect": "^1.13.3",
|
| 1564 |
+
"side-channel-list": "^1.0.0",
|
| 1565 |
+
"side-channel-map": "^1.0.1",
|
| 1566 |
+
"side-channel-weakmap": "^1.0.2"
|
| 1567 |
+
},
|
| 1568 |
+
"engines": {
|
| 1569 |
+
"node": ">= 0.4"
|
| 1570 |
+
},
|
| 1571 |
+
"funding": {
|
| 1572 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 1573 |
+
}
|
| 1574 |
+
},
|
| 1575 |
+
"node_modules/side-channel-list": {
|
| 1576 |
+
"version": "1.0.0",
|
| 1577 |
+
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
|
| 1578 |
+
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
|
| 1579 |
+
"license": "MIT",
|
| 1580 |
+
"optional": true,
|
| 1581 |
+
"dependencies": {
|
| 1582 |
+
"es-errors": "^1.3.0",
|
| 1583 |
+
"object-inspect": "^1.13.3"
|
| 1584 |
+
},
|
| 1585 |
+
"engines": {
|
| 1586 |
+
"node": ">= 0.4"
|
| 1587 |
+
},
|
| 1588 |
+
"funding": {
|
| 1589 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 1590 |
+
}
|
| 1591 |
+
},
|
| 1592 |
+
"node_modules/side-channel-map": {
|
| 1593 |
+
"version": "1.0.1",
|
| 1594 |
+
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
|
| 1595 |
+
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
|
| 1596 |
+
"license": "MIT",
|
| 1597 |
+
"optional": true,
|
| 1598 |
+
"dependencies": {
|
| 1599 |
+
"call-bound": "^1.0.2",
|
| 1600 |
+
"es-errors": "^1.3.0",
|
| 1601 |
+
"get-intrinsic": "^1.2.5",
|
| 1602 |
+
"object-inspect": "^1.13.3"
|
| 1603 |
+
},
|
| 1604 |
+
"engines": {
|
| 1605 |
+
"node": ">= 0.4"
|
| 1606 |
+
},
|
| 1607 |
+
"funding": {
|
| 1608 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 1609 |
+
}
|
| 1610 |
+
},
|
| 1611 |
+
"node_modules/side-channel-weakmap": {
|
| 1612 |
+
"version": "1.0.2",
|
| 1613 |
+
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
|
| 1614 |
+
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
|
| 1615 |
+
"license": "MIT",
|
| 1616 |
+
"optional": true,
|
| 1617 |
+
"dependencies": {
|
| 1618 |
+
"call-bound": "^1.0.2",
|
| 1619 |
+
"es-errors": "^1.3.0",
|
| 1620 |
+
"get-intrinsic": "^1.2.5",
|
| 1621 |
+
"object-inspect": "^1.13.3",
|
| 1622 |
+
"side-channel-map": "^1.0.1"
|
| 1623 |
+
},
|
| 1624 |
+
"engines": {
|
| 1625 |
+
"node": ">= 0.4"
|
| 1626 |
+
},
|
| 1627 |
+
"funding": {
|
| 1628 |
+
"url": "https://github.com/sponsors/ljharb"
|
| 1629 |
+
}
|
| 1630 |
+
},
|
| 1631 |
+
"node_modules/statuses": {
|
| 1632 |
+
"version": "2.0.2",
|
| 1633 |
+
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
|
| 1634 |
+
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
|
| 1635 |
+
"license": "MIT",
|
| 1636 |
+
"optional": true,
|
| 1637 |
+
"engines": {
|
| 1638 |
+
"node": ">= 0.8"
|
| 1639 |
+
}
|
| 1640 |
+
},
|
| 1641 |
+
"node_modules/toidentifier": {
|
| 1642 |
+
"version": "1.0.1",
|
| 1643 |
+
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
| 1644 |
+
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
|
| 1645 |
+
"license": "MIT",
|
| 1646 |
+
"optional": true,
|
| 1647 |
+
"engines": {
|
| 1648 |
+
"node": ">=0.6"
|
| 1649 |
+
}
|
| 1650 |
+
},
|
| 1651 |
+
"node_modules/tsx": {
|
| 1652 |
+
"version": "4.20.3",
|
| 1653 |
+
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.3.tgz",
|
| 1654 |
+
"integrity": "sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==",
|
| 1655 |
+
"license": "MIT",
|
| 1656 |
+
"dependencies": {
|
| 1657 |
+
"esbuild": "~0.25.0",
|
| 1658 |
+
"get-tsconfig": "^4.7.5"
|
| 1659 |
+
},
|
| 1660 |
+
"bin": {
|
| 1661 |
+
"tsx": "dist/cli.mjs"
|
| 1662 |
+
},
|
| 1663 |
+
"engines": {
|
| 1664 |
+
"node": ">=18.0.0"
|
| 1665 |
+
},
|
| 1666 |
+
"optionalDependencies": {
|
| 1667 |
+
"fsevents": "~2.3.3"
|
| 1668 |
+
}
|
| 1669 |
+
},
|
| 1670 |
+
"node_modules/type-is": {
|
| 1671 |
+
"version": "2.0.1",
|
| 1672 |
+
"resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
|
| 1673 |
+
"integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
|
| 1674 |
+
"license": "MIT",
|
| 1675 |
+
"optional": true,
|
| 1676 |
+
"dependencies": {
|
| 1677 |
+
"content-type": "^1.0.5",
|
| 1678 |
+
"media-typer": "^1.1.0",
|
| 1679 |
+
"mime-types": "^3.0.0"
|
| 1680 |
+
},
|
| 1681 |
+
"engines": {
|
| 1682 |
+
"node": ">= 0.6"
|
| 1683 |
+
}
|
| 1684 |
+
},
|
| 1685 |
+
"node_modules/typescript": {
|
| 1686 |
+
"version": "5.8.3",
|
| 1687 |
+
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
|
| 1688 |
+
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
|
| 1689 |
+
"license": "Apache-2.0",
|
| 1690 |
+
"bin": {
|
| 1691 |
+
"tsc": "bin/tsc",
|
| 1692 |
+
"tsserver": "bin/tsserver"
|
| 1693 |
+
},
|
| 1694 |
+
"engines": {
|
| 1695 |
+
"node": ">=14.17"
|
| 1696 |
+
}
|
| 1697 |
+
},
|
| 1698 |
+
"node_modules/undici-types": {
|
| 1699 |
+
"version": "7.8.0",
|
| 1700 |
+
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz",
|
| 1701 |
+
"integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==",
|
| 1702 |
+
"license": "MIT"
|
| 1703 |
+
},
|
| 1704 |
+
"node_modules/unpipe": {
|
| 1705 |
+
"version": "1.0.0",
|
| 1706 |
+
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
| 1707 |
+
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
|
| 1708 |
+
"license": "MIT",
|
| 1709 |
+
"optional": true,
|
| 1710 |
+
"engines": {
|
| 1711 |
+
"node": ">= 0.8"
|
| 1712 |
+
}
|
| 1713 |
+
},
|
| 1714 |
+
"node_modules/uri-js": {
|
| 1715 |
+
"version": "4.4.1",
|
| 1716 |
+
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
| 1717 |
+
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
|
| 1718 |
+
"license": "BSD-2-Clause",
|
| 1719 |
+
"optional": true,
|
| 1720 |
+
"dependencies": {
|
| 1721 |
+
"punycode": "^2.1.0"
|
| 1722 |
+
}
|
| 1723 |
+
},
|
| 1724 |
+
"node_modules/vary": {
|
| 1725 |
+
"version": "1.1.2",
|
| 1726 |
+
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
| 1727 |
+
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
| 1728 |
+
"license": "MIT",
|
| 1729 |
+
"optional": true,
|
| 1730 |
+
"engines": {
|
| 1731 |
+
"node": ">= 0.8"
|
| 1732 |
+
}
|
| 1733 |
+
},
|
| 1734 |
+
"node_modules/which": {
|
| 1735 |
+
"version": "2.0.2",
|
| 1736 |
+
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
| 1737 |
+
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
| 1738 |
+
"license": "ISC",
|
| 1739 |
+
"optional": true,
|
| 1740 |
+
"dependencies": {
|
| 1741 |
+
"isexe": "^2.0.0"
|
| 1742 |
+
},
|
| 1743 |
+
"bin": {
|
| 1744 |
+
"node-which": "bin/node-which"
|
| 1745 |
+
},
|
| 1746 |
+
"engines": {
|
| 1747 |
+
"node": ">= 8"
|
| 1748 |
+
}
|
| 1749 |
+
},
|
| 1750 |
+
"node_modules/wrappy": {
|
| 1751 |
+
"version": "1.0.2",
|
| 1752 |
+
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
| 1753 |
+
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
| 1754 |
+
"license": "ISC",
|
| 1755 |
+
"optional": true
|
| 1756 |
+
},
|
| 1757 |
+
"node_modules/ws": {
|
| 1758 |
+
"version": "8.18.3",
|
| 1759 |
+
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
| 1760 |
+
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
|
| 1761 |
+
"license": "MIT",
|
| 1762 |
+
"engines": {
|
| 1763 |
+
"node": ">=10.0.0"
|
| 1764 |
+
},
|
| 1765 |
+
"peerDependencies": {
|
| 1766 |
+
"bufferutil": "^4.0.1",
|
| 1767 |
+
"utf-8-validate": ">=5.0.2"
|
| 1768 |
+
},
|
| 1769 |
+
"peerDependenciesMeta": {
|
| 1770 |
+
"bufferutil": {
|
| 1771 |
+
"optional": true
|
| 1772 |
+
},
|
| 1773 |
+
"utf-8-validate": {
|
| 1774 |
+
"optional": true
|
| 1775 |
+
}
|
| 1776 |
+
}
|
| 1777 |
+
},
|
| 1778 |
+
"node_modules/zod": {
|
| 1779 |
+
"version": "3.25.67",
|
| 1780 |
+
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz",
|
| 1781 |
+
"integrity": "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==",
|
| 1782 |
+
"license": "MIT",
|
| 1783 |
+
"funding": {
|
| 1784 |
+
"url": "https://github.com/sponsors/colinhacks"
|
| 1785 |
+
}
|
| 1786 |
+
},
|
| 1787 |
+
"node_modules/zod-to-json-schema": {
|
| 1788 |
+
"version": "3.24.6",
|
| 1789 |
+
"resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz",
|
| 1790 |
+
"integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==",
|
| 1791 |
+
"license": "ISC",
|
| 1792 |
+
"optional": true,
|
| 1793 |
+
"peerDependencies": {
|
| 1794 |
+
"zod": "^3.24.1"
|
| 1795 |
+
}
|
| 1796 |
+
}
|
| 1797 |
+
}
|
| 1798 |
+
}
|
examples/agents-sdk-js/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"type": "module",
|
| 3 |
+
"name": "agents-sdk",
|
| 4 |
+
"version": "1.0.0",
|
| 5 |
+
"main": "index.js",
|
| 6 |
+
"scripts": {
|
| 7 |
+
"start": "tsx index.ts",
|
| 8 |
+
"test": "echo \"Error: no test specified\" && exit 1"
|
| 9 |
+
},
|
| 10 |
+
"keywords": [],
|
| 11 |
+
"author": "",
|
| 12 |
+
"license": "ISC",
|
| 13 |
+
"description": "",
|
| 14 |
+
"dependencies": {
|
| 15 |
+
"@openai/agents": "^0.0.14",
|
| 16 |
+
"tsx": "^4.20.3",
|
| 17 |
+
"typescript": "^5.8.3",
|
| 18 |
+
"zod": "^3.25.67"
|
| 19 |
+
}
|
| 20 |
+
}
|
examples/agents-sdk-python/example.py
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import asyncio
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
import shutil
|
| 4 |
+
|
| 5 |
+
from openai import AsyncOpenAI
|
| 6 |
+
from agents import (
|
| 7 |
+
Agent,
|
| 8 |
+
ItemHelpers,
|
| 9 |
+
Runner,
|
| 10 |
+
set_default_openai_api,
|
| 11 |
+
set_default_openai_client,
|
| 12 |
+
set_tracing_disabled,
|
| 13 |
+
function_tool,
|
| 14 |
+
)
|
| 15 |
+
from agents.mcp import MCPServerStdio
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
async def prompt_user(question: str) -> str:
|
| 19 |
+
"""Async input prompt function"""
|
| 20 |
+
loop = asyncio.get_event_loop()
|
| 21 |
+
return await loop.run_in_executor(None, input, question)
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
async def main():
|
| 25 |
+
# Set up OpenAI client for local server (e.g., Ollama)
|
| 26 |
+
openai_client = AsyncOpenAI(
|
| 27 |
+
api_key="local",
|
| 28 |
+
base_url="http://localhost:11434/v1",
|
| 29 |
+
)
|
| 30 |
+
|
| 31 |
+
# Get current working directory
|
| 32 |
+
samples_dir = str(Path.cwd())
|
| 33 |
+
|
| 34 |
+
# Create MCP server for filesystem operations
|
| 35 |
+
mcp_server = MCPServerStdio(
|
| 36 |
+
name="Filesystem MCP Server, via npx",
|
| 37 |
+
params={
|
| 38 |
+
"command": "npx",
|
| 39 |
+
"args": [
|
| 40 |
+
"-y",
|
| 41 |
+
"@modelcontextprotocol/server-filesystem",
|
| 42 |
+
samples_dir,
|
| 43 |
+
],
|
| 44 |
+
},
|
| 45 |
+
)
|
| 46 |
+
|
| 47 |
+
# Connect to MCP server
|
| 48 |
+
await mcp_server.connect()
|
| 49 |
+
|
| 50 |
+
# Configure agents SDK
|
| 51 |
+
set_tracing_disabled(True)
|
| 52 |
+
set_default_openai_client(openai_client)
|
| 53 |
+
set_default_openai_api("chat_completions")
|
| 54 |
+
|
| 55 |
+
# Define weather tool
|
| 56 |
+
@function_tool
|
| 57 |
+
async def get_weather(location: str) -> str:
|
| 58 |
+
return f"The weather in {location} is sunny."
|
| 59 |
+
|
| 60 |
+
# Create agent
|
| 61 |
+
agent = Agent(
|
| 62 |
+
name="My Agent",
|
| 63 |
+
instructions="You are a helpful assistant.",
|
| 64 |
+
tools=[get_weather],
|
| 65 |
+
model="gpt-oss:20b-test",
|
| 66 |
+
mcp_servers=[mcp_server],
|
| 67 |
+
)
|
| 68 |
+
|
| 69 |
+
# Get user input
|
| 70 |
+
user_input = await prompt_user("> ")
|
| 71 |
+
|
| 72 |
+
# Run agent with streaming
|
| 73 |
+
result = Runner.run_streamed(agent, user_input)
|
| 74 |
+
|
| 75 |
+
# Process streaming results
|
| 76 |
+
async for event in result.stream_events():
|
| 77 |
+
if event.type == "raw_response_event":
|
| 78 |
+
continue
|
| 79 |
+
elif event.type == "agent_updated_stream_event":
|
| 80 |
+
print(f"Agent updated: {event.new_agent.name}")
|
| 81 |
+
elif event.type == "run_item_stream_event":
|
| 82 |
+
if event.item.type == "tool_call_item":
|
| 83 |
+
print("-- Tool was called")
|
| 84 |
+
elif event.item.type == "tool_call_output_item":
|
| 85 |
+
print(f"-- Tool output: {event.item.output}")
|
| 86 |
+
elif event.item.type == "message_output_item":
|
| 87 |
+
print(
|
| 88 |
+
f"-- Message output:\n {ItemHelpers.text_message_output(event.item)}"
|
| 89 |
+
)
|
| 90 |
+
else:
|
| 91 |
+
pass
|
| 92 |
+
|
| 93 |
+
print("=== Run complete ===")
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
if __name__ == "__main__":
|
| 97 |
+
|
| 98 |
+
if not shutil.which("npx"):
|
| 99 |
+
raise RuntimeError(
|
| 100 |
+
"npx is not installed. Please install it with `npm install -g npx`."
|
| 101 |
+
)
|
| 102 |
+
asyncio.run(main())
|
examples/agents-sdk-python/pyproject.toml
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[project]
|
| 2 |
+
name = "agents-sdk-python"
|
| 3 |
+
version = "0.1.0"
|
| 4 |
+
description = "Add your description here"
|
| 5 |
+
readme = "README.md"
|
| 6 |
+
requires-python = ">=3.12"
|
| 7 |
+
dependencies = [
|
| 8 |
+
"openai-agents>=0.2.4",
|
| 9 |
+
]
|
examples/gradio/gradio_chat.py
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
import requests
|
| 3 |
+
import gradio as gr
|
| 4 |
+
|
| 5 |
+
DEFAULT_FUNCTION_PROPERTIES = """
|
| 6 |
+
{
|
| 7 |
+
"type": "object",
|
| 8 |
+
"properties": {
|
| 9 |
+
"location": {
|
| 10 |
+
"type": "string",
|
| 11 |
+
"description": "The city and state, e.g. San Francisco, CA"
|
| 12 |
+
}
|
| 13 |
+
},
|
| 14 |
+
"required": ["location"]
|
| 15 |
+
}
|
| 16 |
+
""".strip()
|
| 17 |
+
|
| 18 |
+
def chat_with_model(message, history, model_choice, instructions, effort, use_functions,
|
| 19 |
+
function_name, function_description, function_parameters,
|
| 20 |
+
use_browser_search, temperature, max_output_tokens, debug_mode):
|
| 21 |
+
|
| 22 |
+
if not message.strip():
|
| 23 |
+
return history, ""
|
| 24 |
+
|
| 25 |
+
# Append user message and empty assistant placeholder (idiomatic Gradio pattern)
|
| 26 |
+
history = history + [[message, ""]]
|
| 27 |
+
|
| 28 |
+
# Build messages list from history (excluding the empty assistant placeholder)
|
| 29 |
+
messages = []
|
| 30 |
+
|
| 31 |
+
# Convert history to messages format (excluding the last empty assistant message)
|
| 32 |
+
for user_msg, assistant_msg in history[:-1]:
|
| 33 |
+
if user_msg:
|
| 34 |
+
messages.append({
|
| 35 |
+
"type": "message",
|
| 36 |
+
"role": "user",
|
| 37 |
+
"content": [{"type": "input_text", "text": user_msg}]
|
| 38 |
+
})
|
| 39 |
+
if assistant_msg:
|
| 40 |
+
messages.append({
|
| 41 |
+
"type": "message",
|
| 42 |
+
"role": "assistant",
|
| 43 |
+
"content": [{"type": "output_text", "text": assistant_msg}]
|
| 44 |
+
})
|
| 45 |
+
|
| 46 |
+
# Add current user message
|
| 47 |
+
messages.append({
|
| 48 |
+
"type": "message",
|
| 49 |
+
"role": "user",
|
| 50 |
+
"content": [{"type": "input_text", "text": message}]
|
| 51 |
+
})
|
| 52 |
+
|
| 53 |
+
# Prepare tools
|
| 54 |
+
tools = []
|
| 55 |
+
if use_functions:
|
| 56 |
+
try:
|
| 57 |
+
tools.append({
|
| 58 |
+
"type": "function",
|
| 59 |
+
"name": function_name,
|
| 60 |
+
"description": function_description,
|
| 61 |
+
"parameters": json.loads(function_parameters),
|
| 62 |
+
})
|
| 63 |
+
except json.JSONDecodeError:
|
| 64 |
+
pass
|
| 65 |
+
|
| 66 |
+
if use_browser_search:
|
| 67 |
+
tools.append({"type": "browser_search"})
|
| 68 |
+
|
| 69 |
+
# Get URL based on model (matching streamlit logic)
|
| 70 |
+
options = ["large", "small"]
|
| 71 |
+
URL = ("http://localhost:8081/v1/responses" if model_choice == options[1]
|
| 72 |
+
else "http://localhost:8000/v1/responses")
|
| 73 |
+
|
| 74 |
+
try:
|
| 75 |
+
response = requests.post(
|
| 76 |
+
URL,
|
| 77 |
+
json={
|
| 78 |
+
"input": messages,
|
| 79 |
+
"stream": True,
|
| 80 |
+
"instructions": instructions,
|
| 81 |
+
"reasoning": {"effort": effort},
|
| 82 |
+
"metadata": {"__debug": debug_mode},
|
| 83 |
+
"tools": tools,
|
| 84 |
+
"temperature": temperature,
|
| 85 |
+
"max_output_tokens": max_output_tokens,
|
| 86 |
+
},
|
| 87 |
+
stream=True,
|
| 88 |
+
)
|
| 89 |
+
|
| 90 |
+
full_content = ""
|
| 91 |
+
text_delta = ""
|
| 92 |
+
current_output_index = 0
|
| 93 |
+
in_reasoning = False
|
| 94 |
+
|
| 95 |
+
for line in response.iter_lines(decode_unicode=True):
|
| 96 |
+
if not line or not line.startswith("data:"):
|
| 97 |
+
continue
|
| 98 |
+
data_str = line[len("data:"):].strip()
|
| 99 |
+
if not data_str:
|
| 100 |
+
continue
|
| 101 |
+
|
| 102 |
+
try:
|
| 103 |
+
data = json.loads(data_str)
|
| 104 |
+
except Exception:
|
| 105 |
+
continue
|
| 106 |
+
|
| 107 |
+
event_type = data.get("type", "")
|
| 108 |
+
output_index = data.get("output_index", 0)
|
| 109 |
+
|
| 110 |
+
if event_type == "response.output_item.added":
|
| 111 |
+
current_output_index = output_index
|
| 112 |
+
output_type = data.get("item", {}).get("type", "message")
|
| 113 |
+
text_delta = ""
|
| 114 |
+
|
| 115 |
+
if output_type == "reasoning":
|
| 116 |
+
if not in_reasoning:
|
| 117 |
+
full_content += "🤔 **Thinking...**\n"
|
| 118 |
+
in_reasoning = True
|
| 119 |
+
elif output_type == "message":
|
| 120 |
+
if in_reasoning:
|
| 121 |
+
full_content += "\n\n"
|
| 122 |
+
in_reasoning = False
|
| 123 |
+
|
| 124 |
+
elif event_type == "response.reasoning_text.delta":
|
| 125 |
+
delta = data.get("delta", "")
|
| 126 |
+
full_content += delta
|
| 127 |
+
|
| 128 |
+
# Update last assistant message (idiomatic Gradio pattern)
|
| 129 |
+
history[-1][1] = full_content
|
| 130 |
+
yield history, ""
|
| 131 |
+
|
| 132 |
+
elif event_type == "response.output_text.delta":
|
| 133 |
+
delta = data.get("delta", "")
|
| 134 |
+
full_content += delta
|
| 135 |
+
|
| 136 |
+
# Update last assistant message (idiomatic Gradio pattern)
|
| 137 |
+
history[-1][1] = full_content
|
| 138 |
+
yield history, ""
|
| 139 |
+
|
| 140 |
+
elif event_type == "response.output_item.done":
|
| 141 |
+
item = data.get("item", {})
|
| 142 |
+
if item.get("type") == "function_call":
|
| 143 |
+
function_call_text = f"\n\n🔨 Called `{item.get('name')}`\n**Arguments**\n```json\n{item.get('arguments', '')}\n```"
|
| 144 |
+
full_content += function_call_text
|
| 145 |
+
|
| 146 |
+
# Update last assistant message (idiomatic Gradio pattern)
|
| 147 |
+
history[-1][1] = full_content
|
| 148 |
+
yield history, ""
|
| 149 |
+
|
| 150 |
+
elif item.get("type") == "web_search_call":
|
| 151 |
+
web_search_text = f"\n\n🌐 **Web Search**\n```json\n{json.dumps(item.get('action', {}), indent=2)}\n```\n✅ Done"
|
| 152 |
+
full_content += web_search_text
|
| 153 |
+
|
| 154 |
+
# Update last assistant message (idiomatic Gradio pattern)
|
| 155 |
+
history[-1][1] = full_content
|
| 156 |
+
yield history, ""
|
| 157 |
+
|
| 158 |
+
elif event_type == "response.completed":
|
| 159 |
+
response_data = data.get("response", {})
|
| 160 |
+
if debug_mode:
|
| 161 |
+
debug_info = response_data.get("metadata", {}).get("__debug", "")
|
| 162 |
+
if debug_info:
|
| 163 |
+
full_content += f"\n\n**Debug**\n```\n{debug_info}\n```"
|
| 164 |
+
|
| 165 |
+
# Update last assistant message (idiomatic Gradio pattern)
|
| 166 |
+
history[-1][1] = full_content
|
| 167 |
+
yield history, ""
|
| 168 |
+
break
|
| 169 |
+
|
| 170 |
+
# Return final history and empty string to clear textbox
|
| 171 |
+
return history, ""
|
| 172 |
+
|
| 173 |
+
except Exception as e:
|
| 174 |
+
error_message = f"❌ Error: {str(e)}"
|
| 175 |
+
history[-1][1] = error_message
|
| 176 |
+
return history, ""
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
# Create the Gradio interface
|
| 180 |
+
with gr.Blocks(title="💬 Chatbot") as demo:
|
| 181 |
+
gr.Markdown("# 💬 Chatbot")
|
| 182 |
+
|
| 183 |
+
with gr.Row():
|
| 184 |
+
with gr.Column(scale=3):
|
| 185 |
+
chatbot = gr.Chatbot(height=500)
|
| 186 |
+
|
| 187 |
+
with gr.Row():
|
| 188 |
+
msg = gr.Textbox(placeholder="Type a message...", scale=4, show_label=False)
|
| 189 |
+
send_btn = gr.Button("Send", scale=1)
|
| 190 |
+
|
| 191 |
+
clear_btn = gr.Button("Clear Chat")
|
| 192 |
+
|
| 193 |
+
with gr.Column(scale=1):
|
| 194 |
+
model_choice = gr.Radio(["large", "small"], value="small", label="Model")
|
| 195 |
+
|
| 196 |
+
instructions = gr.Textbox(
|
| 197 |
+
label="Instructions",
|
| 198 |
+
value="You are a helpful assistant that can answer questions and help with tasks.",
|
| 199 |
+
lines=3
|
| 200 |
+
)
|
| 201 |
+
|
| 202 |
+
effort = gr.Radio(["low", "medium", "high"], value="medium", label="Reasoning effort")
|
| 203 |
+
|
| 204 |
+
gr.Markdown("#### Functions")
|
| 205 |
+
use_functions = gr.Checkbox(label="Use functions", value=False)
|
| 206 |
+
|
| 207 |
+
with gr.Column(visible=False) as function_group:
|
| 208 |
+
function_name = gr.Textbox(label="Function name", value="get_weather")
|
| 209 |
+
function_description = gr.Textbox(
|
| 210 |
+
label="Function description",
|
| 211 |
+
value="Get the weather for a given city"
|
| 212 |
+
)
|
| 213 |
+
function_parameters = gr.Textbox(
|
| 214 |
+
label="Function parameters",
|
| 215 |
+
value=DEFAULT_FUNCTION_PROPERTIES,
|
| 216 |
+
lines=6
|
| 217 |
+
)
|
| 218 |
+
|
| 219 |
+
# Conditional browser search (matching Streamlit logic)
|
| 220 |
+
# In Streamlit: if "show_browser" in st.query_params:
|
| 221 |
+
# For Gradio, we'll always show it (simplified)
|
| 222 |
+
gr.Markdown("#### Built-in Tools")
|
| 223 |
+
use_browser_search = gr.Checkbox(label="Use browser search", value=False)
|
| 224 |
+
|
| 225 |
+
temperature = gr.Slider(0.0, 1.0, value=1.0, step=0.01, label="Temperature")
|
| 226 |
+
max_output_tokens = gr.Slider(1000, 20000, value=1024, step=100, label="Max output tokens")
|
| 227 |
+
|
| 228 |
+
debug_mode = gr.Checkbox(label="Debug mode", value=False)
|
| 229 |
+
|
| 230 |
+
# Event handlers
|
| 231 |
+
def toggle_function_group(use_funcs):
|
| 232 |
+
return gr.update(visible=use_funcs)
|
| 233 |
+
|
| 234 |
+
use_functions.change(toggle_function_group, use_functions, function_group)
|
| 235 |
+
|
| 236 |
+
# Chat functionality
|
| 237 |
+
inputs = [msg, chatbot, model_choice, instructions, effort, use_functions,
|
| 238 |
+
function_name, function_description, function_parameters,
|
| 239 |
+
use_browser_search, temperature, max_output_tokens, debug_mode]
|
| 240 |
+
|
| 241 |
+
msg.submit(chat_with_model, inputs, [chatbot, msg])
|
| 242 |
+
send_btn.click(chat_with_model, inputs, [chatbot, msg])
|
| 243 |
+
clear_btn.click(lambda: [], outputs=chatbot)
|
| 244 |
+
|
| 245 |
+
|
| 246 |
+
if __name__ == "__main__":
|
| 247 |
+
demo.launch()
|
examples/streamlit/streamlit_chat.py
ADDED
|
@@ -0,0 +1,354 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json
|
| 2 |
+
|
| 3 |
+
import requests
|
| 4 |
+
import streamlit as st
|
| 5 |
+
|
| 6 |
+
DEFAULT_FUNCTION_PROPERTIES = """
|
| 7 |
+
{
|
| 8 |
+
"type": "object",
|
| 9 |
+
"properties": {
|
| 10 |
+
"location": {
|
| 11 |
+
"type": "string",
|
| 12 |
+
"description": "The city and state, e.g. San Francisco, CA"
|
| 13 |
+
}
|
| 14 |
+
},
|
| 15 |
+
"required": ["location"]
|
| 16 |
+
}
|
| 17 |
+
""".strip()
|
| 18 |
+
|
| 19 |
+
# Session state for chat
|
| 20 |
+
if "messages" not in st.session_state:
|
| 21 |
+
st.session_state.messages = []
|
| 22 |
+
|
| 23 |
+
st.title("💬 Chatbot")
|
| 24 |
+
|
| 25 |
+
if "model" not in st.session_state:
|
| 26 |
+
if "model" in st.query_params:
|
| 27 |
+
st.session_state.model = st.query_params["model"]
|
| 28 |
+
else:
|
| 29 |
+
st.session_state.model = "small"
|
| 30 |
+
|
| 31 |
+
options = ["large", "small"]
|
| 32 |
+
selection = st.sidebar.segmented_control(
|
| 33 |
+
"Model", options, selection_mode="single", default=st.session_state.model
|
| 34 |
+
)
|
| 35 |
+
# st.session_state.model = selection
|
| 36 |
+
st.query_params.update({"model": selection})
|
| 37 |
+
|
| 38 |
+
instructions = st.sidebar.text_area(
|
| 39 |
+
"Instructions",
|
| 40 |
+
value="You are a helpful assistant that can answer questions and help with tasks.",
|
| 41 |
+
)
|
| 42 |
+
effort = st.sidebar.radio(
|
| 43 |
+
"Reasoning effort",
|
| 44 |
+
["low", "medium", "high"],
|
| 45 |
+
index=1,
|
| 46 |
+
)
|
| 47 |
+
st.sidebar.divider()
|
| 48 |
+
st.sidebar.subheader("Functions")
|
| 49 |
+
use_functions = st.sidebar.toggle("Use functions", value=False)
|
| 50 |
+
|
| 51 |
+
st.sidebar.subheader("Built-in Tools")
|
| 52 |
+
# Built-in Tools section
|
| 53 |
+
use_browser_search = st.sidebar.toggle("Use browser search", value=False)
|
| 54 |
+
use_code_interpreter = st.sidebar.toggle("Use code interpreter", value=False)
|
| 55 |
+
|
| 56 |
+
if use_functions:
|
| 57 |
+
function_name = st.sidebar.text_input("Function name", value="get_weather")
|
| 58 |
+
function_description = st.sidebar.text_area(
|
| 59 |
+
"Function description", value="Get the weather for a given city"
|
| 60 |
+
)
|
| 61 |
+
function_parameters = st.sidebar.text_area(
|
| 62 |
+
"Function parameters", value=DEFAULT_FUNCTION_PROPERTIES
|
| 63 |
+
)
|
| 64 |
+
else:
|
| 65 |
+
function_name = None
|
| 66 |
+
function_description = None
|
| 67 |
+
function_parameters = None
|
| 68 |
+
st.sidebar.divider()
|
| 69 |
+
temperature = st.sidebar.slider(
|
| 70 |
+
"Temperature", min_value=0.0, max_value=1.0, value=1.0, step=0.01
|
| 71 |
+
)
|
| 72 |
+
max_output_tokens = st.sidebar.slider(
|
| 73 |
+
"Max output tokens", min_value=1, max_value=131072, value=30000, step=1000
|
| 74 |
+
)
|
| 75 |
+
st.sidebar.divider()
|
| 76 |
+
debug_mode = st.sidebar.toggle("Debug mode", value=False)
|
| 77 |
+
|
| 78 |
+
if debug_mode:
|
| 79 |
+
st.sidebar.divider()
|
| 80 |
+
st.sidebar.code(json.dumps(st.session_state.messages, indent=2), "json")
|
| 81 |
+
|
| 82 |
+
render_input = True
|
| 83 |
+
|
| 84 |
+
URL = (
|
| 85 |
+
"http://localhost:8081/v1/responses"
|
| 86 |
+
if selection == options[1]
|
| 87 |
+
else "http://localhost:8000/v1/responses"
|
| 88 |
+
)
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def trigger_fake_tool(container):
|
| 92 |
+
function_output = st.session_state.get("function_output", "It's sunny!")
|
| 93 |
+
last_call = st.session_state.messages[-1]
|
| 94 |
+
if last_call.get("type") == "function_call":
|
| 95 |
+
st.session_state.messages.append(
|
| 96 |
+
{
|
| 97 |
+
"type": "function_call_output",
|
| 98 |
+
"call_id": last_call.get("call_id"),
|
| 99 |
+
"output": function_output,
|
| 100 |
+
}
|
| 101 |
+
)
|
| 102 |
+
run(container)
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
def run(container):
|
| 106 |
+
tools = []
|
| 107 |
+
if use_functions:
|
| 108 |
+
tools.append(
|
| 109 |
+
{
|
| 110 |
+
"type": "function",
|
| 111 |
+
"name": function_name,
|
| 112 |
+
"description": function_description,
|
| 113 |
+
"parameters": json.loads(function_parameters),
|
| 114 |
+
}
|
| 115 |
+
)
|
| 116 |
+
# Add browser_search tool if checkbox is checked
|
| 117 |
+
if use_browser_search:
|
| 118 |
+
tools.append({"type": "browser_search"})
|
| 119 |
+
if use_code_interpreter:
|
| 120 |
+
tools.append({"type": "code_interpreter"})
|
| 121 |
+
response = requests.post(
|
| 122 |
+
URL,
|
| 123 |
+
json={
|
| 124 |
+
"input": st.session_state.messages,
|
| 125 |
+
"stream": True,
|
| 126 |
+
"instructions": instructions,
|
| 127 |
+
"reasoning": {"effort": effort},
|
| 128 |
+
"metadata": {"__debug": debug_mode},
|
| 129 |
+
"tools": tools,
|
| 130 |
+
"temperature": temperature,
|
| 131 |
+
"max_output_tokens": max_output_tokens,
|
| 132 |
+
},
|
| 133 |
+
stream=True,
|
| 134 |
+
)
|
| 135 |
+
|
| 136 |
+
text_delta = ""
|
| 137 |
+
code_interpreter_sessions: dict[str, dict] = {}
|
| 138 |
+
|
| 139 |
+
_current_output_index = 0
|
| 140 |
+
for line in response.iter_lines(decode_unicode=True):
|
| 141 |
+
if not line or not line.startswith("data:"):
|
| 142 |
+
continue
|
| 143 |
+
data_str = line[len("data:") :].strip()
|
| 144 |
+
if not data_str:
|
| 145 |
+
continue
|
| 146 |
+
try:
|
| 147 |
+
data = json.loads(data_str)
|
| 148 |
+
except Exception:
|
| 149 |
+
continue
|
| 150 |
+
|
| 151 |
+
event_type = data.get("type", "")
|
| 152 |
+
output_index = data.get("output_index", 0)
|
| 153 |
+
if event_type == "response.output_item.added":
|
| 154 |
+
_current_output_index = output_index
|
| 155 |
+
output_type = data.get("item", {}).get("type", "message")
|
| 156 |
+
if output_type == "message":
|
| 157 |
+
output = container.chat_message("assistant")
|
| 158 |
+
placeholder = output.empty()
|
| 159 |
+
elif output_type == "reasoning":
|
| 160 |
+
output = container.chat_message("reasoning", avatar="🤔")
|
| 161 |
+
placeholder = output.empty()
|
| 162 |
+
elif output_type == "web_search_call":
|
| 163 |
+
output = container.chat_message("web_search_call", avatar="🌐")
|
| 164 |
+
output.code(
|
| 165 |
+
json.dumps(data.get("item", {}).get("action", {}), indent=4),
|
| 166 |
+
language="json",
|
| 167 |
+
)
|
| 168 |
+
placeholder = output.empty()
|
| 169 |
+
elif output_type == "code_interpreter_call":
|
| 170 |
+
item = data.get("item", {})
|
| 171 |
+
item_id = item.get("id")
|
| 172 |
+
message_container = container.chat_message(
|
| 173 |
+
"code_interpreter_call", avatar="🧪"
|
| 174 |
+
)
|
| 175 |
+
status_placeholder = message_container.empty()
|
| 176 |
+
code_placeholder = message_container.empty()
|
| 177 |
+
outputs_container = message_container.container()
|
| 178 |
+
code_text = item.get("code") or ""
|
| 179 |
+
if code_text:
|
| 180 |
+
code_placeholder.code(code_text, language="python")
|
| 181 |
+
code_interpreter_sessions[item_id] = {
|
| 182 |
+
"status": status_placeholder,
|
| 183 |
+
"code": code_placeholder,
|
| 184 |
+
"outputs": outputs_container,
|
| 185 |
+
"code_text": code_text,
|
| 186 |
+
"rendered_outputs": False,
|
| 187 |
+
}
|
| 188 |
+
placeholder = status_placeholder
|
| 189 |
+
text_delta = ""
|
| 190 |
+
elif event_type == "response.reasoning_text.delta":
|
| 191 |
+
output.avatar = "🤔"
|
| 192 |
+
text_delta += data.get("delta", "")
|
| 193 |
+
placeholder.markdown(text_delta)
|
| 194 |
+
elif event_type == "response.output_text.delta":
|
| 195 |
+
text_delta += data.get("delta", "")
|
| 196 |
+
placeholder.markdown(text_delta)
|
| 197 |
+
elif event_type == "response.output_item.done":
|
| 198 |
+
item = data.get("item", {})
|
| 199 |
+
if item.get("type") == "function_call":
|
| 200 |
+
with container.chat_message("function_call", avatar="🔨"):
|
| 201 |
+
st.markdown(f"Called `{item.get('name')}`")
|
| 202 |
+
st.caption("Arguments")
|
| 203 |
+
st.code(item.get("arguments", ""), language="json")
|
| 204 |
+
if item.get("type") == "web_search_call":
|
| 205 |
+
placeholder.markdown("✅ Done")
|
| 206 |
+
if item.get("type") == "code_interpreter_call":
|
| 207 |
+
item_id = item.get("id")
|
| 208 |
+
session = code_interpreter_sessions.get(item_id)
|
| 209 |
+
if session:
|
| 210 |
+
session["status"].markdown("✅ Done")
|
| 211 |
+
final_code = item.get("code") or session["code_text"]
|
| 212 |
+
if final_code:
|
| 213 |
+
session["code"].code(final_code, language="python")
|
| 214 |
+
session["code_text"] = final_code
|
| 215 |
+
outputs = item.get("outputs") or []
|
| 216 |
+
if outputs and not session["rendered_outputs"]:
|
| 217 |
+
with session["outputs"]:
|
| 218 |
+
st.markdown("**Outputs**")
|
| 219 |
+
for output_item in outputs:
|
| 220 |
+
output_type = output_item.get("type")
|
| 221 |
+
if output_type == "logs":
|
| 222 |
+
st.code(
|
| 223 |
+
output_item.get("logs", ""),
|
| 224 |
+
language="text",
|
| 225 |
+
)
|
| 226 |
+
elif output_type == "image":
|
| 227 |
+
st.image(
|
| 228 |
+
output_item.get("url", ""),
|
| 229 |
+
caption="Code interpreter image",
|
| 230 |
+
)
|
| 231 |
+
session["rendered_outputs"] = True
|
| 232 |
+
elif not outputs and not session["rendered_outputs"]:
|
| 233 |
+
with session["outputs"]:
|
| 234 |
+
st.caption("(No outputs)")
|
| 235 |
+
session["rendered_outputs"] = True
|
| 236 |
+
else:
|
| 237 |
+
placeholder.markdown("✅ Done")
|
| 238 |
+
elif event_type == "response.code_interpreter_call.in_progress":
|
| 239 |
+
item_id = data.get("item_id")
|
| 240 |
+
session = code_interpreter_sessions.get(item_id)
|
| 241 |
+
if session:
|
| 242 |
+
session["status"].markdown("⏳ Running")
|
| 243 |
+
else:
|
| 244 |
+
try:
|
| 245 |
+
placeholder.markdown("⏳ Running")
|
| 246 |
+
except Exception:
|
| 247 |
+
pass
|
| 248 |
+
elif event_type == "response.code_interpreter_call.interpreting":
|
| 249 |
+
item_id = data.get("item_id")
|
| 250 |
+
session = code_interpreter_sessions.get(item_id)
|
| 251 |
+
if session:
|
| 252 |
+
session["status"].markdown("🧮 Interpreting")
|
| 253 |
+
elif event_type == "response.code_interpreter_call.completed":
|
| 254 |
+
item_id = data.get("item_id")
|
| 255 |
+
session = code_interpreter_sessions.get(item_id)
|
| 256 |
+
if session:
|
| 257 |
+
session["status"].markdown("✅ Done")
|
| 258 |
+
else:
|
| 259 |
+
try:
|
| 260 |
+
placeholder.markdown("✅ Done")
|
| 261 |
+
except Exception:
|
| 262 |
+
pass
|
| 263 |
+
elif event_type == "response.code_interpreter_call_code.delta":
|
| 264 |
+
item_id = data.get("item_id")
|
| 265 |
+
session = code_interpreter_sessions.get(item_id)
|
| 266 |
+
if session:
|
| 267 |
+
session["code_text"] += data.get("delta", "")
|
| 268 |
+
if session["code_text"].strip():
|
| 269 |
+
session["code"].code(session["code_text"], language="python")
|
| 270 |
+
elif event_type == "response.code_interpreter_call_code.done":
|
| 271 |
+
item_id = data.get("item_id")
|
| 272 |
+
session = code_interpreter_sessions.get(item_id)
|
| 273 |
+
if session:
|
| 274 |
+
final_code = data.get("code") or session["code_text"]
|
| 275 |
+
session["code_text"] = final_code
|
| 276 |
+
if final_code:
|
| 277 |
+
session["code"].code(final_code, language="python")
|
| 278 |
+
elif event_type == "response.completed":
|
| 279 |
+
response = data.get("response", {})
|
| 280 |
+
if debug_mode:
|
| 281 |
+
container.expander("Debug", expanded=False).code(
|
| 282 |
+
response.get("metadata", {}).get("__debug", ""), language="text"
|
| 283 |
+
)
|
| 284 |
+
st.session_state.messages.extend(response.get("output", []))
|
| 285 |
+
if st.session_state.messages[-1].get("type") == "function_call":
|
| 286 |
+
with container.form("function_output_form"):
|
| 287 |
+
_function_output = st.text_input(
|
| 288 |
+
"Enter function output",
|
| 289 |
+
value=st.session_state.get("function_output", "It's sunny!"),
|
| 290 |
+
key="function_output",
|
| 291 |
+
)
|
| 292 |
+
st.form_submit_button(
|
| 293 |
+
"Submit function output",
|
| 294 |
+
on_click=trigger_fake_tool,
|
| 295 |
+
args=[container],
|
| 296 |
+
)
|
| 297 |
+
# Optionally handle other event types...
|
| 298 |
+
|
| 299 |
+
|
| 300 |
+
# Chat display
|
| 301 |
+
for msg in st.session_state.messages:
|
| 302 |
+
if msg.get("type") == "message":
|
| 303 |
+
with st.chat_message(msg["role"]):
|
| 304 |
+
for item in msg["content"]:
|
| 305 |
+
if (
|
| 306 |
+
item.get("type") == "text"
|
| 307 |
+
or item.get("type") == "output_text"
|
| 308 |
+
or item.get("type") == "input_text"
|
| 309 |
+
):
|
| 310 |
+
st.markdown(item["text"])
|
| 311 |
+
if item.get("annotations"):
|
| 312 |
+
annotation_lines = "\n".join(
|
| 313 |
+
f"- {annotation.get('url')}"
|
| 314 |
+
for annotation in item["annotations"]
|
| 315 |
+
if annotation.get("url")
|
| 316 |
+
)
|
| 317 |
+
st.caption(f"**Annotations:**\n{annotation_lines}")
|
| 318 |
+
elif msg.get("type") == "reasoning":
|
| 319 |
+
with st.chat_message("reasoning", avatar="🤔"):
|
| 320 |
+
for item in msg["content"]:
|
| 321 |
+
if item.get("type") == "reasoning_text":
|
| 322 |
+
st.markdown(item["text"])
|
| 323 |
+
elif msg.get("type") == "function_call":
|
| 324 |
+
with st.chat_message("function_call", avatar="🔨"):
|
| 325 |
+
st.markdown(f"Called `{msg.get('name')}`")
|
| 326 |
+
st.caption("Arguments")
|
| 327 |
+
st.code(msg.get("arguments", ""), language="json")
|
| 328 |
+
elif msg.get("type") == "function_call_output":
|
| 329 |
+
with st.chat_message("function_call_output", avatar="✅"):
|
| 330 |
+
st.caption("Output")
|
| 331 |
+
st.code(msg.get("output", ""), language="text")
|
| 332 |
+
elif msg.get("type") == "web_search_call":
|
| 333 |
+
with st.chat_message("web_search_call", avatar="🌐"):
|
| 334 |
+
st.code(json.dumps(msg.get("action", {}), indent=4), language="json")
|
| 335 |
+
st.markdown("✅ Done")
|
| 336 |
+
elif msg.get("type") == "code_interpreter_call":
|
| 337 |
+
with st.chat_message("code_interpreter_call", avatar="🧪"):
|
| 338 |
+
st.markdown("✅ Done")
|
| 339 |
+
|
| 340 |
+
if render_input:
|
| 341 |
+
# Input field
|
| 342 |
+
if prompt := st.chat_input("Type a message..."):
|
| 343 |
+
st.session_state.messages.append(
|
| 344 |
+
{
|
| 345 |
+
"type": "message",
|
| 346 |
+
"role": "user",
|
| 347 |
+
"content": [{"type": "input_text", "text": prompt}],
|
| 348 |
+
}
|
| 349 |
+
)
|
| 350 |
+
|
| 351 |
+
with st.chat_message("user"):
|
| 352 |
+
st.markdown(prompt)
|
| 353 |
+
|
| 354 |
+
run(st.container())
|
gpt-oss-mcp-server/README.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# MCP Servers for gpt-oss reference tools
|
| 2 |
+
|
| 3 |
+
This directory contains MCP servers for the reference tools in the [gpt-oss](https://github.com/openai/gpt-oss) repository.
|
| 4 |
+
You can set up these tools behind MCP servers and use them in your applications.
|
| 5 |
+
For inference service that integrates with MCP, you can also use these as reference tools.
|
| 6 |
+
|
| 7 |
+
In particular, this directory contains a `build-system-prompt.py` script that will generate exactly the same system prompt as `reference-system-prompt.py`.
|
| 8 |
+
The build system prompt script show case all the care needed to automatically discover the tools and construct the system prompt before feeding it into Harmony.
|
| 9 |
+
|
| 10 |
+
## Usage
|
| 11 |
+
|
| 12 |
+
```bash
|
| 13 |
+
# Install the dependencies
|
| 14 |
+
uv pip install -r requirements.txt
|
| 15 |
+
```
|
| 16 |
+
|
| 17 |
+
```bash
|
| 18 |
+
# Assume we have harmony and gpt-oss installed
|
| 19 |
+
uv pip install mcp[cli]
|
| 20 |
+
# start the servers
|
| 21 |
+
mcp run -t sse browser_server.py:mcp
|
| 22 |
+
mcp run -t sse python_server.py:mcp
|
| 23 |
+
```
|
| 24 |
+
|
| 25 |
+
You can now use MCP inspector to play with the tools.
|
| 26 |
+
Once opened, set SSE to `http://localhost:8001/sse` and `http://localhost:8000/sse` respectively.
|
| 27 |
+
|
| 28 |
+
To compare the system prompt and see how to construct it via MCP service discovery, see `build-system-prompt.py`.
|
| 29 |
+
This script will generate exactly the same system prompt as `reference-system-prompt.py`.
|
gpt-oss-mcp-server/browser_server.py
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from collections.abc import AsyncIterator
|
| 3 |
+
from contextlib import asynccontextmanager
|
| 4 |
+
from dataclasses import dataclass, field
|
| 5 |
+
from typing import Union, Optional
|
| 6 |
+
|
| 7 |
+
from mcp.server.fastmcp import Context, FastMCP
|
| 8 |
+
from gpt_oss.tools.simple_browser import SimpleBrowserTool
|
| 9 |
+
from gpt_oss.tools.simple_browser.backend import YouComBackend, ExaBackend
|
| 10 |
+
|
| 11 |
+
@dataclass
|
| 12 |
+
class AppContext:
|
| 13 |
+
browsers: dict[str, SimpleBrowserTool] = field(default_factory=dict)
|
| 14 |
+
|
| 15 |
+
def create_or_get_browser(self, session_id: str) -> SimpleBrowserTool:
|
| 16 |
+
if session_id not in self.browsers:
|
| 17 |
+
tool_backend = os.getenv("BROWSER_BACKEND", "exa")
|
| 18 |
+
if tool_backend == "youcom":
|
| 19 |
+
backend = YouComBackend(source="web")
|
| 20 |
+
elif tool_backend == "exa":
|
| 21 |
+
backend = ExaBackend(source="web")
|
| 22 |
+
else:
|
| 23 |
+
raise ValueError(f"Invalid tool backend: {tool_backend}")
|
| 24 |
+
self.browsers[session_id] = SimpleBrowserTool(backend=backend)
|
| 25 |
+
return self.browsers[session_id]
|
| 26 |
+
|
| 27 |
+
def remove_browser(self, session_id: str) -> None:
|
| 28 |
+
self.browsers.pop(session_id, None)
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
@asynccontextmanager
|
| 32 |
+
async def app_lifespan(_server: FastMCP) -> AsyncIterator[AppContext]:
|
| 33 |
+
yield AppContext()
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
# Pass lifespan to server
|
| 37 |
+
mcp = FastMCP(
|
| 38 |
+
name="browser",
|
| 39 |
+
instructions=r"""
|
| 40 |
+
Tool for browsing.
|
| 41 |
+
The `cursor` appears in brackets before each browsing display: `[{cursor}]`.
|
| 42 |
+
Cite information from the tool using the following format:
|
| 43 |
+
`【{cursor}†L{line_start}(-L{line_end})?】`, for example: `【6†L9-L11】` or `【8†L3】`.
|
| 44 |
+
Do not quote more than 10 words directly from the tool output.
|
| 45 |
+
sources=web
|
| 46 |
+
""".strip(),
|
| 47 |
+
lifespan=app_lifespan,
|
| 48 |
+
port=8001,
|
| 49 |
+
)
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
@mcp.tool(
|
| 53 |
+
name="search",
|
| 54 |
+
title="Search for information",
|
| 55 |
+
description=
|
| 56 |
+
"Searches for information related to `query` and displays `topn` results.",
|
| 57 |
+
)
|
| 58 |
+
async def search(ctx: Context,
|
| 59 |
+
query: str,
|
| 60 |
+
topn: int = 10,
|
| 61 |
+
source: Optional[str] = None) -> str:
|
| 62 |
+
"""Search for information related to a query"""
|
| 63 |
+
browser = ctx.request_context.lifespan_context.create_or_get_browser(
|
| 64 |
+
ctx.client_id)
|
| 65 |
+
messages = []
|
| 66 |
+
async for message in browser.search(query=query, topn=topn, source=source):
|
| 67 |
+
if message.content and hasattr(message.content[0], 'text'):
|
| 68 |
+
messages.append(message.content[0].text)
|
| 69 |
+
return "\n".join(messages)
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
@mcp.tool(
|
| 73 |
+
name="open",
|
| 74 |
+
title="Open a link or page",
|
| 75 |
+
description="""
|
| 76 |
+
Opens the link `id` from the page indicated by `cursor` starting at line number `loc`, showing `num_lines` lines.
|
| 77 |
+
Valid link ids are displayed with the formatting: `【{id}†.*】`.
|
| 78 |
+
If `cursor` is not provided, the most recent page is implied.
|
| 79 |
+
If `id` is a string, it is treated as a fully qualified URL associated with `source`.
|
| 80 |
+
If `loc` is not provided, the viewport will be positioned at the beginning of the document or centered on the most relevant passage, if available.
|
| 81 |
+
Use this function without `id` to scroll to a new location of an opened page.
|
| 82 |
+
""".strip(),
|
| 83 |
+
)
|
| 84 |
+
async def open_link(ctx: Context,
|
| 85 |
+
id: Union[int, str] = -1,
|
| 86 |
+
cursor: int = -1,
|
| 87 |
+
loc: int = -1,
|
| 88 |
+
num_lines: int = -1,
|
| 89 |
+
view_source: bool = False,
|
| 90 |
+
source: Optional[str] = None) -> str:
|
| 91 |
+
"""Open a link or navigate to a page location"""
|
| 92 |
+
browser = ctx.request_context.lifespan_context.create_or_get_browser(
|
| 93 |
+
ctx.client_id)
|
| 94 |
+
messages = []
|
| 95 |
+
async for message in browser.open(id=id,
|
| 96 |
+
cursor=cursor,
|
| 97 |
+
loc=loc,
|
| 98 |
+
num_lines=num_lines,
|
| 99 |
+
view_source=view_source,
|
| 100 |
+
source=source):
|
| 101 |
+
if message.content and hasattr(message.content[0], 'text'):
|
| 102 |
+
messages.append(message.content[0].text)
|
| 103 |
+
return "\n".join(messages)
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
@mcp.tool(
|
| 107 |
+
name="find",
|
| 108 |
+
title="Find pattern in page",
|
| 109 |
+
description=
|
| 110 |
+
"Finds exact matches of `pattern` in the current page, or the page given by `cursor`.",
|
| 111 |
+
)
|
| 112 |
+
async def find_pattern(ctx: Context, pattern: str, cursor: int = -1) -> str:
|
| 113 |
+
"""Find exact matches of a pattern in the current page"""
|
| 114 |
+
browser = ctx.request_context.lifespan_context.create_or_get_browser(
|
| 115 |
+
ctx.client_id)
|
| 116 |
+
messages = []
|
| 117 |
+
async for message in browser.find(pattern=pattern, cursor=cursor):
|
| 118 |
+
if message.content and hasattr(message.content[0], 'text'):
|
| 119 |
+
messages.append(message.content[0].text)
|
| 120 |
+
return "\n".join(messages)
|
gpt-oss-mcp-server/build-system-prompt.py
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import datetime
|
| 2 |
+
import asyncio
|
| 3 |
+
|
| 4 |
+
from gpt_oss.tokenizer import get_tokenizer
|
| 5 |
+
|
| 6 |
+
from openai_harmony import (
|
| 7 |
+
Conversation,
|
| 8 |
+
DeveloperContent,
|
| 9 |
+
HarmonyEncodingName,
|
| 10 |
+
Message,
|
| 11 |
+
ReasoningEffort,
|
| 12 |
+
Role,
|
| 13 |
+
SystemContent,
|
| 14 |
+
ToolNamespaceConfig,
|
| 15 |
+
ToolDescription,
|
| 16 |
+
load_harmony_encoding,
|
| 17 |
+
)
|
| 18 |
+
|
| 19 |
+
from mcp import ClientSession
|
| 20 |
+
from mcp.client.sse import sse_client
|
| 21 |
+
from mcp.types import ListToolsResult
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
async def list_server_and_tools(server_url: str):
|
| 25 |
+
async with sse_client(url=server_url) as streams, ClientSession(
|
| 26 |
+
*streams) as session:
|
| 27 |
+
initialize_response = await session.initialize()
|
| 28 |
+
list_tools_response = await session.list_tools()
|
| 29 |
+
return initialize_response, list_tools_response
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def trim_schema(schema: dict) -> dict:
|
| 33 |
+
# Turn JSON Schema from MCP generated into Harmony's variant.
|
| 34 |
+
if "title" in schema:
|
| 35 |
+
del schema["title"]
|
| 36 |
+
if "default" in schema and schema["default"] is None:
|
| 37 |
+
del schema["default"]
|
| 38 |
+
if "anyOf" in schema:
|
| 39 |
+
# Turn "anyOf": [{"type": "type-1"}, {"type": "type-2"}] into "type": ["type-1", "type-2"]
|
| 40 |
+
# if there's more than 1 types, also remove "null" type as Harmony will just ignore it
|
| 41 |
+
types = [
|
| 42 |
+
type_dict["type"] for type_dict in schema["anyOf"]
|
| 43 |
+
if type_dict["type"] != 'null'
|
| 44 |
+
]
|
| 45 |
+
schema["type"] = types
|
| 46 |
+
del schema["anyOf"]
|
| 47 |
+
if "properties" in schema:
|
| 48 |
+
schema["properties"] = {
|
| 49 |
+
k: trim_schema(v)
|
| 50 |
+
for k, v in schema["properties"].items()
|
| 51 |
+
}
|
| 52 |
+
return schema
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def post_process_tools_description(
|
| 56 |
+
list_tools_result: ListToolsResult) -> ListToolsResult:
|
| 57 |
+
# Adapt the MCP tool result for Harmony
|
| 58 |
+
for tool in list_tools_result.tools:
|
| 59 |
+
tool.inputSchema = trim_schema(tool.inputSchema)
|
| 60 |
+
|
| 61 |
+
# Some tools schema don't need to be part of the prompt (e.g. simple text in text out for Python)
|
| 62 |
+
list_tools_result.tools = [
|
| 63 |
+
tool for tool in list_tools_result.tools
|
| 64 |
+
if getattr(tool.annotations, "include_in_prompt", True)
|
| 65 |
+
]
|
| 66 |
+
|
| 67 |
+
return list_tools_result
|
| 68 |
+
|
| 69 |
+
tokenizer = get_tokenizer()
|
| 70 |
+
|
| 71 |
+
tools_urls = [
|
| 72 |
+
"http://localhost:8001/sse", # browser
|
| 73 |
+
"http://localhost:8000/sse", # python
|
| 74 |
+
]
|
| 75 |
+
harmony_tool_descriptions = []
|
| 76 |
+
for tools_url in tools_urls:
|
| 77 |
+
|
| 78 |
+
initialize_response, list_tools_response = asyncio.run(
|
| 79 |
+
list_server_and_tools(tools_url))
|
| 80 |
+
|
| 81 |
+
list_tools_response = post_process_tools_description(list_tools_response)
|
| 82 |
+
|
| 83 |
+
tool_from_mcp = ToolNamespaceConfig(
|
| 84 |
+
name=initialize_response.serverInfo.name,
|
| 85 |
+
description=initialize_response.instructions,
|
| 86 |
+
tools=[
|
| 87 |
+
ToolDescription.new(name=tool.name,
|
| 88 |
+
description=tool.description,
|
| 89 |
+
parameters=tool.inputSchema)
|
| 90 |
+
for tool in list_tools_response.tools
|
| 91 |
+
])
|
| 92 |
+
harmony_tool_descriptions.append(tool_from_mcp)
|
| 93 |
+
|
| 94 |
+
encoding = load_harmony_encoding(HarmonyEncodingName.HARMONY_GPT_OSS)
|
| 95 |
+
|
| 96 |
+
system_message_content = (SystemContent.new().with_reasoning_effort(
|
| 97 |
+
ReasoningEffort.LOW).with_conversation_start_date(
|
| 98 |
+
datetime.datetime.now().strftime("%Y-%m-%d")))
|
| 99 |
+
|
| 100 |
+
for tool_description in harmony_tool_descriptions:
|
| 101 |
+
system_message_content = system_message_content.with_tools(
|
| 102 |
+
tool_description)
|
| 103 |
+
|
| 104 |
+
system_message = Message.from_role_and_content(Role.SYSTEM,
|
| 105 |
+
system_message_content)
|
| 106 |
+
|
| 107 |
+
developer_message_content = DeveloperContent.new().with_instructions("")
|
| 108 |
+
developer_message = Message.from_role_and_content(Role.DEVELOPER,
|
| 109 |
+
developer_message_content)
|
| 110 |
+
|
| 111 |
+
messages = [system_message, developer_message]
|
| 112 |
+
|
| 113 |
+
conversation = Conversation.from_messages(messages)
|
| 114 |
+
tokens = encoding.render_conversation(conversation)
|
| 115 |
+
system_message = tokenizer.decode(tokens)
|
| 116 |
+
print(system_message)
|
gpt-oss-mcp-server/pyproject.toml
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[project]
|
| 2 |
+
name = "gpt-oss-mcp-server"
|
| 3 |
+
version = "0.1.0"
|
| 4 |
+
requires-python = ">=3.10"
|
| 5 |
+
dependencies = [
|
| 6 |
+
"mcp[cli]>=1.12.2",
|
| 7 |
+
# "gpt_oss"
|
| 8 |
+
]
|
gpt-oss-mcp-server/python_server.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from mcp.server.fastmcp import FastMCP
|
| 2 |
+
from gpt_oss.tools.python_docker.docker_tool import PythonTool
|
| 3 |
+
from openai_harmony import Message, TextContent, Author, Role
|
| 4 |
+
|
| 5 |
+
# Pass lifespan to server
|
| 6 |
+
mcp = FastMCP(
|
| 7 |
+
name="python",
|
| 8 |
+
instructions=r"""
|
| 9 |
+
Use this tool to execute Python code in your chain of thought. The code will not be shown to the user. This tool should be used for internal reasoning, but not for code that is intended to be visible to the user (e.g. when creating plots, tables, or files).
|
| 10 |
+
When you send a message containing python code to python, it will be executed in a stateless docker container, and the stdout of that process will be returned to you.
|
| 11 |
+
""".strip(),
|
| 12 |
+
)
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
@mcp.tool(
|
| 16 |
+
name="python",
|
| 17 |
+
title="Execute Python code",
|
| 18 |
+
description="""
|
| 19 |
+
Use this tool to execute Python code in your chain of thought. The code will not be shown to the user. This tool should be used for internal reasoning, but not for code that is intended to be visible to the user (e.g. when creating plots, tables, or files).
|
| 20 |
+
When you send a message containing python code to python, it will be executed in a stateless docker container, and the stdout of that process will be returned to you.
|
| 21 |
+
""",
|
| 22 |
+
annotations={
|
| 23 |
+
# Harmony format don't want this schema to be part of it because it's simple text in text out
|
| 24 |
+
"include_in_prompt": False,
|
| 25 |
+
})
|
| 26 |
+
async def python(code: str) -> str:
|
| 27 |
+
tool = PythonTool()
|
| 28 |
+
messages = []
|
| 29 |
+
async for message in tool.process(
|
| 30 |
+
Message(author=Author(role=Role.TOOL, name="python"),
|
| 31 |
+
content=[TextContent(text=code)])):
|
| 32 |
+
messages.append(message)
|
| 33 |
+
return "\n".join([message.content[0].text for message in messages])
|
gpt-oss-mcp-server/reference-system-prompt.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import datetime
|
| 2 |
+
|
| 3 |
+
from gpt_oss.tools.simple_browser import SimpleBrowserTool
|
| 4 |
+
from gpt_oss.tools.simple_browser.backend import YouComBackend
|
| 5 |
+
from gpt_oss.tools.python_docker.docker_tool import PythonTool
|
| 6 |
+
from gpt_oss.tokenizer import tokenizer
|
| 7 |
+
|
| 8 |
+
from openai_harmony import (
|
| 9 |
+
Conversation,
|
| 10 |
+
DeveloperContent,
|
| 11 |
+
HarmonyEncodingName,
|
| 12 |
+
Message,
|
| 13 |
+
ReasoningEffort,
|
| 14 |
+
Role,
|
| 15 |
+
SystemContent,
|
| 16 |
+
load_harmony_encoding,
|
| 17 |
+
)
|
| 18 |
+
|
| 19 |
+
encoding = load_harmony_encoding(HarmonyEncodingName.HARMONY_GPT_OSS)
|
| 20 |
+
|
| 21 |
+
system_message_content = (SystemContent.new().with_reasoning_effort(
|
| 22 |
+
ReasoningEffort.LOW).with_conversation_start_date(
|
| 23 |
+
datetime.datetime.now().strftime("%Y-%m-%d")))
|
| 24 |
+
|
| 25 |
+
backend = YouComBackend(source="web")
|
| 26 |
+
browser_tool = SimpleBrowserTool(backend=backend)
|
| 27 |
+
system_message_content = system_message_content.with_tools(
|
| 28 |
+
browser_tool.tool_config)
|
| 29 |
+
|
| 30 |
+
python_tool = PythonTool()
|
| 31 |
+
system_message_content = system_message_content.with_tools(
|
| 32 |
+
python_tool.tool_config)
|
| 33 |
+
|
| 34 |
+
system_message = Message.from_role_and_content(Role.SYSTEM,
|
| 35 |
+
system_message_content)
|
| 36 |
+
|
| 37 |
+
developer_message_content = DeveloperContent.new().with_instructions("")
|
| 38 |
+
developer_message = Message.from_role_and_content(Role.DEVELOPER,
|
| 39 |
+
developer_message_content)
|
| 40 |
+
|
| 41 |
+
messages = [system_message, developer_message]
|
| 42 |
+
|
| 43 |
+
conversation = Conversation.from_messages(messages)
|
| 44 |
+
tokens = encoding.render_conversation(conversation)
|
| 45 |
+
system_message = tokenizer.decode(tokens)
|
| 46 |
+
print(system_message)
|
gpt_oss/__init__.py
ADDED
|
File without changes
|
gpt_oss/chat.py
ADDED
|
@@ -0,0 +1,369 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Harmony chat with tools
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
import atexit
|
| 6 |
+
import argparse
|
| 7 |
+
import asyncio
|
| 8 |
+
import datetime
|
| 9 |
+
import os
|
| 10 |
+
from pathlib import Path
|
| 11 |
+
|
| 12 |
+
try:
|
| 13 |
+
import gnureadline as readline
|
| 14 |
+
except ImportError:
|
| 15 |
+
import readline
|
| 16 |
+
|
| 17 |
+
import torch
|
| 18 |
+
import termcolor
|
| 19 |
+
|
| 20 |
+
from gpt_oss.tools import apply_patch
|
| 21 |
+
from gpt_oss.tools.simple_browser import SimpleBrowserTool
|
| 22 |
+
from gpt_oss.tools.simple_browser.backend import YouComBackend
|
| 23 |
+
from gpt_oss.tools.python_docker.docker_tool import PythonTool
|
| 24 |
+
|
| 25 |
+
from openai_harmony import (
|
| 26 |
+
Author,
|
| 27 |
+
Conversation,
|
| 28 |
+
DeveloperContent,
|
| 29 |
+
HarmonyEncodingName,
|
| 30 |
+
Message,
|
| 31 |
+
ReasoningEffort,
|
| 32 |
+
Role,
|
| 33 |
+
StreamableParser,
|
| 34 |
+
StreamState,
|
| 35 |
+
SystemContent,
|
| 36 |
+
TextContent,
|
| 37 |
+
ToolDescription,
|
| 38 |
+
load_harmony_encoding,
|
| 39 |
+
)
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
REASONING_EFFORT = {
|
| 43 |
+
"high": ReasoningEffort.HIGH,
|
| 44 |
+
"medium": ReasoningEffort.MEDIUM,
|
| 45 |
+
"low": ReasoningEffort.LOW,
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def get_user_input():
|
| 50 |
+
rank = torch.distributed.get_rank() if torch.distributed.is_initialized() else 0
|
| 51 |
+
if rank == 0:
|
| 52 |
+
user_input = input()
|
| 53 |
+
else:
|
| 54 |
+
user_input = ""
|
| 55 |
+
user_input_list = [user_input]
|
| 56 |
+
if torch.distributed.is_initialized():
|
| 57 |
+
torch.distributed.broadcast_object_list(user_input_list, 0)
|
| 58 |
+
return user_input_list[0]
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def main(args):
|
| 62 |
+
match args.backend:
|
| 63 |
+
case "triton":
|
| 64 |
+
from gpt_oss.triton.model import TokenGenerator as TritonGenerator
|
| 65 |
+
from gpt_oss.torch.utils import init_distributed
|
| 66 |
+
device = init_distributed()
|
| 67 |
+
generator = TritonGenerator(args.checkpoint, args.context, device)
|
| 68 |
+
case "torch":
|
| 69 |
+
from gpt_oss.torch.model import TokenGenerator as TorchGenerator
|
| 70 |
+
from gpt_oss.torch.utils import init_distributed
|
| 71 |
+
device = init_distributed()
|
| 72 |
+
generator = TorchGenerator(args.checkpoint, device)
|
| 73 |
+
case "vllm":
|
| 74 |
+
from gpt_oss.vllm.token_generator import TokenGenerator as VLLMGenerator
|
| 75 |
+
generator = VLLMGenerator(args.checkpoint, tensor_parallel_size=2)
|
| 76 |
+
case _:
|
| 77 |
+
raise ValueError(f"Invalid backend: {args.backend}")
|
| 78 |
+
|
| 79 |
+
encoding = load_harmony_encoding(HarmonyEncodingName.HARMONY_GPT_OSS)
|
| 80 |
+
|
| 81 |
+
system_message_content = (
|
| 82 |
+
SystemContent.new()
|
| 83 |
+
.with_reasoning_effort(REASONING_EFFORT[args.reasoning_effort])
|
| 84 |
+
.with_conversation_start_date(datetime.datetime.now().strftime("%Y-%m-%d"))
|
| 85 |
+
)
|
| 86 |
+
|
| 87 |
+
if args.browser:
|
| 88 |
+
backend = YouComBackend(
|
| 89 |
+
source="web",
|
| 90 |
+
)
|
| 91 |
+
browser_tool = SimpleBrowserTool(backend=backend)
|
| 92 |
+
system_message_content = system_message_content.with_tools(browser_tool.tool_config)
|
| 93 |
+
|
| 94 |
+
if args.python:
|
| 95 |
+
python_tool = PythonTool()
|
| 96 |
+
system_message_content = system_message_content.with_tools(python_tool.tool_config)
|
| 97 |
+
|
| 98 |
+
system_message = Message.from_role_and_content(Role.SYSTEM, system_message_content)
|
| 99 |
+
messages = [system_message]
|
| 100 |
+
|
| 101 |
+
if args.apply_patch:
|
| 102 |
+
apply_patch_instructions = Path(apply_patch.__file__).parent / "apply_patch.md"
|
| 103 |
+
developer_message = ""
|
| 104 |
+
if args.developer_message:
|
| 105 |
+
developer_message = args.developer_message + "\n"
|
| 106 |
+
developer_message += apply_patch_instructions.read_text()
|
| 107 |
+
developer_message_content = (
|
| 108 |
+
DeveloperContent.new()
|
| 109 |
+
.with_instructions(developer_message)
|
| 110 |
+
.with_function_tools([
|
| 111 |
+
ToolDescription.new(
|
| 112 |
+
"apply_patch",
|
| 113 |
+
"Patch a file",
|
| 114 |
+
parameters={
|
| 115 |
+
"type": "string",
|
| 116 |
+
"description": "Formatted patch code",
|
| 117 |
+
"default": "*** Begin Patch\n*** End Patch\n",
|
| 118 |
+
}
|
| 119 |
+
),
|
| 120 |
+
])
|
| 121 |
+
)
|
| 122 |
+
messages.append(Message.from_role_and_content(Role.DEVELOPER, developer_message_content))
|
| 123 |
+
elif args.developer_message:
|
| 124 |
+
developer_message_content = DeveloperContent.new().with_instructions(args.developer_message)
|
| 125 |
+
messages.append(Message.from_role_and_content(Role.DEVELOPER, developer_message_content))
|
| 126 |
+
else:
|
| 127 |
+
developer_message_content = None
|
| 128 |
+
|
| 129 |
+
if args.raw:
|
| 130 |
+
conversation = Conversation.from_messages(messages)
|
| 131 |
+
tokens = encoding.render_conversation(conversation)
|
| 132 |
+
system_message = encoding.decode(tokens)
|
| 133 |
+
print(system_message, flush=True, end="")
|
| 134 |
+
empty_user_message_tokens = encoding.render(Message.from_role_and_content(Role.USER, ""))
|
| 135 |
+
user_message_start = encoding.decode(empty_user_message_tokens[:-1])
|
| 136 |
+
user_message_end = encoding.decode(empty_user_message_tokens[-1:])
|
| 137 |
+
else:
|
| 138 |
+
# System message
|
| 139 |
+
print(termcolor.colored("System Message:", "cyan"), flush=True)
|
| 140 |
+
print(termcolor.colored("Model Identity:", "cyan"), system_message_content.model_identity, flush=True)
|
| 141 |
+
print(termcolor.colored("Reasoning Effort:", "cyan"), system_message_content.reasoning_effort, flush=True)
|
| 142 |
+
print(termcolor.colored("Conversation Start Date:", "cyan"), system_message_content.conversation_start_date, flush=True)
|
| 143 |
+
print(termcolor.colored("Knowledge Cutoff:", "cyan"), system_message_content.knowledge_cutoff, flush=True)
|
| 144 |
+
print(termcolor.colored("Browser Tool:", "cyan"), "Enabled" if args.browser else "Disabled", flush=True)
|
| 145 |
+
print(termcolor.colored("Python Tool:", "cyan"), "Enabled" if args.python else "Disabled", flush=True)
|
| 146 |
+
print(termcolor.colored("Apply Patch Function:", "cyan"), "Enabled" if args.apply_patch else "Disabled", flush=True)
|
| 147 |
+
if developer_message_content:
|
| 148 |
+
print(termcolor.colored("Developer Message:", "yellow"), flush=True)
|
| 149 |
+
print(developer_message_content.instructions, flush=True)
|
| 150 |
+
|
| 151 |
+
# Print the system message and the user message start
|
| 152 |
+
MESSAGE_PADDING = 12
|
| 153 |
+
while True:
|
| 154 |
+
last_message = messages[-1]
|
| 155 |
+
if last_message.recipient is None:
|
| 156 |
+
if args.raw:
|
| 157 |
+
print(user_message_start, end="", flush=True)
|
| 158 |
+
user_message = get_user_input()
|
| 159 |
+
print(user_message_end, flush=True, end="")
|
| 160 |
+
else:
|
| 161 |
+
print(termcolor.colored("User:".ljust(MESSAGE_PADDING), "red"), flush=True)
|
| 162 |
+
user_message = get_user_input()
|
| 163 |
+
user_message = Message.from_role_and_content(Role.USER, user_message)
|
| 164 |
+
messages.append(user_message)
|
| 165 |
+
else:
|
| 166 |
+
# Tool or function call
|
| 167 |
+
if last_message.recipient.startswith("browser."):
|
| 168 |
+
assert args.browser, "Browser tool is not enabled"
|
| 169 |
+
tool_name = "Search"
|
| 170 |
+
async def run_tool():
|
| 171 |
+
results = []
|
| 172 |
+
async for msg in browser_tool.process(last_message):
|
| 173 |
+
results.append(msg)
|
| 174 |
+
return results
|
| 175 |
+
|
| 176 |
+
result = asyncio.run(run_tool())
|
| 177 |
+
messages += result
|
| 178 |
+
elif last_message.recipient.startswith("python"):
|
| 179 |
+
assert args.python, "Python tool is not enabled"
|
| 180 |
+
tool_name = "Python"
|
| 181 |
+
async def run_tool():
|
| 182 |
+
results = []
|
| 183 |
+
async for msg in python_tool.process(last_message):
|
| 184 |
+
results.append(msg)
|
| 185 |
+
return results
|
| 186 |
+
|
| 187 |
+
result = asyncio.run(run_tool())
|
| 188 |
+
messages += result
|
| 189 |
+
elif last_message.recipient == "functions.apply_patch":
|
| 190 |
+
assert args.apply_patch, "Apply patch tool is not enabled"
|
| 191 |
+
tool_name = "Apply Patch"
|
| 192 |
+
text = last_message.content[0].text
|
| 193 |
+
tool_output = None
|
| 194 |
+
|
| 195 |
+
if text.startswith("{"):
|
| 196 |
+
# this is json, try to extract the patch from it
|
| 197 |
+
import json
|
| 198 |
+
try:
|
| 199 |
+
some_dict = json.loads(text)
|
| 200 |
+
_, text = some_dict.popitem()
|
| 201 |
+
except Exception as e:
|
| 202 |
+
tool_output = f"Error parsing JSON: {e}"
|
| 203 |
+
|
| 204 |
+
if tool_output is None:
|
| 205 |
+
try:
|
| 206 |
+
tool_output = apply_patch.apply_patch(text)
|
| 207 |
+
except Exception as e:
|
| 208 |
+
tool_output = f"Error applying patch: {e}"
|
| 209 |
+
|
| 210 |
+
message = (
|
| 211 |
+
Message(
|
| 212 |
+
author=Author.new(Role.TOOL, last_message.recipient),
|
| 213 |
+
content=[TextContent(text=tool_output)]
|
| 214 |
+
)
|
| 215 |
+
.with_recipient("assistant")
|
| 216 |
+
)
|
| 217 |
+
if last_message.channel:
|
| 218 |
+
message = message.with_channel(last_message.channel)
|
| 219 |
+
|
| 220 |
+
result = [message]
|
| 221 |
+
messages += result
|
| 222 |
+
else:
|
| 223 |
+
raise ValueError(f"Unknown tool or function call: {last_message.recipient}")
|
| 224 |
+
# Print the tool or function call result
|
| 225 |
+
if args.raw:
|
| 226 |
+
rendered_result = encoding.render_conversation(Conversation.from_messages(result))
|
| 227 |
+
print(encoding.decode(rendered_result), flush=True, end="")
|
| 228 |
+
else:
|
| 229 |
+
print(termcolor.colored(f"{tool_name} output:".ljust(MESSAGE_PADDING), "magenta"), flush=True)
|
| 230 |
+
if tool_name == "Search" and not args.show_browser_results:
|
| 231 |
+
print("[Search results fed to the model]")
|
| 232 |
+
else:
|
| 233 |
+
print(result[0].content[0].text)
|
| 234 |
+
|
| 235 |
+
conversation = Conversation.from_messages(messages)
|
| 236 |
+
tokens = encoding.render_conversation_for_completion(
|
| 237 |
+
conversation, Role.ASSISTANT
|
| 238 |
+
)
|
| 239 |
+
|
| 240 |
+
if args.raw:
|
| 241 |
+
# Print the last two tokens, which are the start of the assistant message
|
| 242 |
+
print(encoding.decode(tokens[-2:]), flush=True, end="")
|
| 243 |
+
|
| 244 |
+
parser = StreamableParser(encoding, role=Role.ASSISTANT)
|
| 245 |
+
field_created = False
|
| 246 |
+
current_output_text = ""
|
| 247 |
+
output_text_delta_buffer = ""
|
| 248 |
+
for predicted_token in generator.generate(tokens, encoding.stop_tokens_for_assistant_actions()):
|
| 249 |
+
parser.process(predicted_token)
|
| 250 |
+
if args.raw:
|
| 251 |
+
print(encoding.decode([predicted_token]), end="", flush=True)
|
| 252 |
+
continue
|
| 253 |
+
|
| 254 |
+
if parser.state == StreamState.EXPECT_START:
|
| 255 |
+
print("") # new line
|
| 256 |
+
field_created = False
|
| 257 |
+
|
| 258 |
+
if not parser.last_content_delta:
|
| 259 |
+
continue
|
| 260 |
+
|
| 261 |
+
if not field_created:
|
| 262 |
+
field_created = True
|
| 263 |
+
if parser.current_channel == "final":
|
| 264 |
+
print(termcolor.colored("Assistant:", "green"), flush=True)
|
| 265 |
+
elif parser.current_recipient is not None:
|
| 266 |
+
print(termcolor.colored(f"Tool call to {parser.current_recipient}:", "cyan"), flush=True)
|
| 267 |
+
else:
|
| 268 |
+
print(termcolor.colored("CoT:", "yellow"), flush=True)
|
| 269 |
+
|
| 270 |
+
should_send_output_text_delta = True
|
| 271 |
+
output_text_delta_buffer += parser.last_content_delta
|
| 272 |
+
if args.browser:
|
| 273 |
+
updated_output_text, _annotations, has_partial_citations = browser_tool.normalize_citations(current_output_text + output_text_delta_buffer)
|
| 274 |
+
output_text_delta_buffer = updated_output_text[len(current_output_text):]
|
| 275 |
+
if has_partial_citations:
|
| 276 |
+
should_send_output_text_delta = False
|
| 277 |
+
if should_send_output_text_delta:
|
| 278 |
+
print(output_text_delta_buffer, end="", flush=True)
|
| 279 |
+
current_output_text += output_text_delta_buffer
|
| 280 |
+
output_text_delta_buffer = ""
|
| 281 |
+
|
| 282 |
+
messages += parser.messages
|
| 283 |
+
|
| 284 |
+
|
| 285 |
+
if __name__ == "__main__":
|
| 286 |
+
parser = argparse.ArgumentParser(
|
| 287 |
+
description="Chat example",
|
| 288 |
+
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
| 289 |
+
)
|
| 290 |
+
parser.add_argument(
|
| 291 |
+
"checkpoint",
|
| 292 |
+
metavar="FILE",
|
| 293 |
+
type=str,
|
| 294 |
+
help="Path to the SafeTensors checkpoint",
|
| 295 |
+
)
|
| 296 |
+
parser.add_argument(
|
| 297 |
+
"-r",
|
| 298 |
+
"--reasoning-effort",
|
| 299 |
+
metavar="REASONING_EFFORT",
|
| 300 |
+
type=str,
|
| 301 |
+
default="low",
|
| 302 |
+
choices=["high", "medium", "low"],
|
| 303 |
+
help="Reasoning effort",
|
| 304 |
+
)
|
| 305 |
+
parser.add_argument(
|
| 306 |
+
"-a",
|
| 307 |
+
"--apply-patch",
|
| 308 |
+
action="store_true",
|
| 309 |
+
help="Make apply_patch function available to the model",
|
| 310 |
+
)
|
| 311 |
+
parser.add_argument(
|
| 312 |
+
"-b",
|
| 313 |
+
"--browser",
|
| 314 |
+
default=False,
|
| 315 |
+
action="store_true",
|
| 316 |
+
help="Use browser tool",
|
| 317 |
+
)
|
| 318 |
+
parser.add_argument(
|
| 319 |
+
"--show-browser-results",
|
| 320 |
+
default=False,
|
| 321 |
+
action="store_true",
|
| 322 |
+
help="Show browser results",
|
| 323 |
+
)
|
| 324 |
+
parser.add_argument(
|
| 325 |
+
"-p",
|
| 326 |
+
"--python",
|
| 327 |
+
default=False,
|
| 328 |
+
action="store_true",
|
| 329 |
+
help="Use python tool",
|
| 330 |
+
)
|
| 331 |
+
parser.add_argument(
|
| 332 |
+
"--developer-message",
|
| 333 |
+
default="",
|
| 334 |
+
help="Developer message",
|
| 335 |
+
)
|
| 336 |
+
parser.add_argument(
|
| 337 |
+
"-c",
|
| 338 |
+
"--context",
|
| 339 |
+
metavar="CONTEXT",
|
| 340 |
+
type=int,
|
| 341 |
+
default=8192,
|
| 342 |
+
help="Max context length",
|
| 343 |
+
)
|
| 344 |
+
parser.add_argument(
|
| 345 |
+
"--raw",
|
| 346 |
+
default=False,
|
| 347 |
+
action="store_true",
|
| 348 |
+
help="Raw mode (does not render Harmony encoding)",
|
| 349 |
+
)
|
| 350 |
+
parser.add_argument(
|
| 351 |
+
"--backend",
|
| 352 |
+
type=str,
|
| 353 |
+
default="triton",
|
| 354 |
+
choices=["triton", "torch", "vllm"],
|
| 355 |
+
help="Inference backend",
|
| 356 |
+
)
|
| 357 |
+
args = parser.parse_args()
|
| 358 |
+
|
| 359 |
+
if int(os.environ.get("WORLD_SIZE", 1)) == 1:
|
| 360 |
+
histfile = os.path.join(os.path.expanduser("~"), ".chat")
|
| 361 |
+
try:
|
| 362 |
+
readline.read_history_file(histfile)
|
| 363 |
+
readline.set_history_length(10000)
|
| 364 |
+
except FileNotFoundError:
|
| 365 |
+
pass
|
| 366 |
+
|
| 367 |
+
atexit.register(readline.write_history_file, histfile)
|
| 368 |
+
|
| 369 |
+
main(args)
|
gpt_oss/evals/README.md
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# `gpt_oss.evals`
|
| 2 |
+
|
| 3 |
+
This module is a reincarnation of [simple-evals](https://github.com/openai/simple-evals) adapted for gpt-oss. It lets you
|
| 4 |
+
run GPQA and HealthBench against a runtime that supports Responses API on `localhost:8080/v1`.
|
gpt_oss/evals/__init__.py
ADDED
|
File without changes
|
gpt_oss/evals/__main__.py
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
import json
|
| 3 |
+
from datetime import datetime
|
| 4 |
+
|
| 5 |
+
from . import report
|
| 6 |
+
from .basic_eval import BasicEval
|
| 7 |
+
from .gpqa_eval import GPQAEval
|
| 8 |
+
from .aime_eval import AIME25Eval
|
| 9 |
+
from .healthbench_eval import HealthBenchEval
|
| 10 |
+
from .chat_completions_sampler import (
|
| 11 |
+
OPENAI_SYSTEM_MESSAGE_API,
|
| 12 |
+
ChatCompletionsSampler,
|
| 13 |
+
)
|
| 14 |
+
from .responses_sampler import ResponsesSampler
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def main():
|
| 18 |
+
parser = argparse.ArgumentParser(
|
| 19 |
+
description="Evaluate the models.",
|
| 20 |
+
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
| 21 |
+
)
|
| 22 |
+
parser.add_argument(
|
| 23 |
+
"--model",
|
| 24 |
+
type=str,
|
| 25 |
+
default="gpt-oss-120b,gpt-oss-20b",
|
| 26 |
+
help="Select a model by name. Accepts a comma-separated list.",
|
| 27 |
+
)
|
| 28 |
+
parser.add_argument(
|
| 29 |
+
"--reasoning-effort",
|
| 30 |
+
type=str,
|
| 31 |
+
default="low,medium,high",
|
| 32 |
+
help="Reasoning effort (low, medium, high). Accepts a comma-separated list.",
|
| 33 |
+
)
|
| 34 |
+
parser.add_argument(
|
| 35 |
+
"--sampler",
|
| 36 |
+
type=str,
|
| 37 |
+
choices=["responses", "chat_completions"],
|
| 38 |
+
default="responses",
|
| 39 |
+
help="Sampler backend to use for models.",
|
| 40 |
+
)
|
| 41 |
+
parser.add_argument(
|
| 42 |
+
"--base-url",
|
| 43 |
+
type=str,
|
| 44 |
+
default="http://localhost:8000/v1",
|
| 45 |
+
help="Base URL for the API.",
|
| 46 |
+
)
|
| 47 |
+
parser.add_argument(
|
| 48 |
+
"--eval",
|
| 49 |
+
type=str,
|
| 50 |
+
default="gpqa,healthbench,healthbench_hard,healthbench_consensus,aime25",
|
| 51 |
+
help="Select an eval by name. Accepts a comma-separated list.",
|
| 52 |
+
)
|
| 53 |
+
parser.add_argument(
|
| 54 |
+
"--temperature",
|
| 55 |
+
type=float,
|
| 56 |
+
default=1.0,
|
| 57 |
+
help="Sampling temperature",
|
| 58 |
+
)
|
| 59 |
+
parser.add_argument(
|
| 60 |
+
"--n-threads",
|
| 61 |
+
type=int,
|
| 62 |
+
default=1584,
|
| 63 |
+
help="Number of threads to run.",
|
| 64 |
+
)
|
| 65 |
+
parser.add_argument(
|
| 66 |
+
"--debug", action="store_true", help="Run in debug mode"
|
| 67 |
+
)
|
| 68 |
+
parser.add_argument(
|
| 69 |
+
"--examples", type=int, help="Number of examples to use (overrides default)"
|
| 70 |
+
)
|
| 71 |
+
|
| 72 |
+
args = parser.parse_args()
|
| 73 |
+
|
| 74 |
+
sampler_cls = ResponsesSampler if args.sampler == "responses" else ChatCompletionsSampler
|
| 75 |
+
|
| 76 |
+
models = {}
|
| 77 |
+
for model_name in args.model.split(","):
|
| 78 |
+
for reasoning_effort in args.reasoning_effort.split(","):
|
| 79 |
+
models[f"{model_name}-{reasoning_effort}"] = sampler_cls(
|
| 80 |
+
model=model_name,
|
| 81 |
+
reasoning_model=True,
|
| 82 |
+
reasoning_effort=reasoning_effort,
|
| 83 |
+
temperature=args.temperature,
|
| 84 |
+
base_url=args.base_url,
|
| 85 |
+
max_tokens=131_072,
|
| 86 |
+
)
|
| 87 |
+
|
| 88 |
+
print(f"Running with args {args}")
|
| 89 |
+
|
| 90 |
+
grading_sampler = ChatCompletionsSampler(
|
| 91 |
+
model="gpt-4.1-2025-04-14",
|
| 92 |
+
system_message=OPENAI_SYSTEM_MESSAGE_API,
|
| 93 |
+
max_tokens=2048,
|
| 94 |
+
base_url="https://api.openai.com/v1",
|
| 95 |
+
)
|
| 96 |
+
|
| 97 |
+
def get_evals(eval_name, debug_mode):
|
| 98 |
+
num_examples = (
|
| 99 |
+
args.examples if args.examples is not None else (5 if debug_mode else None)
|
| 100 |
+
)
|
| 101 |
+
# Set num_examples = None to reproduce full evals
|
| 102 |
+
match eval_name:
|
| 103 |
+
case "basic":
|
| 104 |
+
return BasicEval()
|
| 105 |
+
case "gpqa":
|
| 106 |
+
return GPQAEval(
|
| 107 |
+
n_repeats=1 if args.debug else 8,
|
| 108 |
+
num_examples=num_examples,
|
| 109 |
+
debug=debug_mode,
|
| 110 |
+
n_threads=args.n_threads or 1,
|
| 111 |
+
)
|
| 112 |
+
case "healthbench":
|
| 113 |
+
return HealthBenchEval(
|
| 114 |
+
grader_model=grading_sampler,
|
| 115 |
+
num_examples=10 if debug_mode else num_examples,
|
| 116 |
+
n_repeats=1,
|
| 117 |
+
n_threads=args.n_threads or 1,
|
| 118 |
+
subset_name=None,
|
| 119 |
+
)
|
| 120 |
+
case "healthbench_hard":
|
| 121 |
+
return HealthBenchEval(
|
| 122 |
+
grader_model=grading_sampler,
|
| 123 |
+
num_examples=10 if debug_mode else num_examples,
|
| 124 |
+
n_repeats=1,
|
| 125 |
+
n_threads=args.n_threads or 1,
|
| 126 |
+
subset_name="hard",
|
| 127 |
+
)
|
| 128 |
+
case "healthbench_consensus":
|
| 129 |
+
return HealthBenchEval(
|
| 130 |
+
grader_model=grading_sampler,
|
| 131 |
+
num_examples=10 if debug_mode else num_examples,
|
| 132 |
+
n_repeats=1,
|
| 133 |
+
n_threads=args.n_threads or 1,
|
| 134 |
+
subset_name="consensus",
|
| 135 |
+
)
|
| 136 |
+
case "aime25":
|
| 137 |
+
return AIME25Eval(
|
| 138 |
+
n_repeats=1 if args.debug else 8,
|
| 139 |
+
num_examples=num_examples,
|
| 140 |
+
n_threads=args.n_threads or 1,
|
| 141 |
+
)
|
| 142 |
+
case _:
|
| 143 |
+
raise Exception(f"Unrecognized eval type: {eval_name}")
|
| 144 |
+
|
| 145 |
+
evals = {}
|
| 146 |
+
for eval_name in args.eval.split(","):
|
| 147 |
+
evals[eval_name] = get_evals(eval_name, args.debug)
|
| 148 |
+
|
| 149 |
+
debug_suffix = "_DEBUG" if args.debug else ""
|
| 150 |
+
print(debug_suffix)
|
| 151 |
+
mergekey2resultpath = {}
|
| 152 |
+
print(f"Running the following evals: {evals}")
|
| 153 |
+
print(f"Running evals for the following models: {models}")
|
| 154 |
+
|
| 155 |
+
now = datetime.now()
|
| 156 |
+
date_str = now.strftime("%Y%m%d_%H%M%S")
|
| 157 |
+
for model_name, sampler in models.items():
|
| 158 |
+
model_name = model_name.replace("/", "__")
|
| 159 |
+
for eval_name, eval_obj in evals.items():
|
| 160 |
+
result = eval_obj(sampler)
|
| 161 |
+
# ^^^ how to use a sampler
|
| 162 |
+
file_stem = f"{eval_name}_{model_name}_temp{args.temperature}"
|
| 163 |
+
# file stem should also include the year, month, day, and time in hours and minutes
|
| 164 |
+
file_stem += f"_{date_str}"
|
| 165 |
+
report_filename = f"/tmp/{file_stem}{debug_suffix}.html"
|
| 166 |
+
print(f"Writing report to {report_filename}")
|
| 167 |
+
with open(report_filename, "w") as fh:
|
| 168 |
+
fh.write(report.make_report(result))
|
| 169 |
+
assert result.metrics is not None
|
| 170 |
+
metrics = result.metrics | {"score": result.score}
|
| 171 |
+
# Sort metrics by key
|
| 172 |
+
metrics = dict(sorted(metrics.items()))
|
| 173 |
+
print(metrics)
|
| 174 |
+
result_filename = f"/tmp/{file_stem}{debug_suffix}.json"
|
| 175 |
+
with open(result_filename, "w") as f:
|
| 176 |
+
f.write(json.dumps(metrics, indent=2))
|
| 177 |
+
print(f"Writing results to {result_filename}")
|
| 178 |
+
|
| 179 |
+
full_result_filename = f"/tmp/{file_stem}{debug_suffix}_allresults.json"
|
| 180 |
+
with open(full_result_filename, "w") as f:
|
| 181 |
+
result_dict = {
|
| 182 |
+
"score": result.score,
|
| 183 |
+
"metrics": result.metrics,
|
| 184 |
+
"htmls": result.htmls,
|
| 185 |
+
"convos": result.convos,
|
| 186 |
+
"metadata": result.metadata,
|
| 187 |
+
}
|
| 188 |
+
f.write(json.dumps(result_dict, indent=2))
|
| 189 |
+
print(f"Writing all results to {full_result_filename}")
|
| 190 |
+
|
| 191 |
+
mergekey2resultpath[f"{file_stem}"] = result_filename
|
| 192 |
+
|
| 193 |
+
merge_metrics = []
|
| 194 |
+
for eval_model_name, result_filename in mergekey2resultpath.items():
|
| 195 |
+
try:
|
| 196 |
+
result = json.load(open(result_filename, "r+"))
|
| 197 |
+
except Exception as e:
|
| 198 |
+
print(e, result_filename)
|
| 199 |
+
continue
|
| 200 |
+
result = result.get("f1_score", result.get("score", None))
|
| 201 |
+
eval_name = eval_model_name[: eval_model_name.find("_")]
|
| 202 |
+
model_name = eval_model_name[eval_model_name.find("_") + 1 :]
|
| 203 |
+
merge_metrics.append(
|
| 204 |
+
{"eval_name": eval_name, "model_name": model_name, "metric": result}
|
| 205 |
+
)
|
| 206 |
+
print(merge_metrics)
|
| 207 |
+
return merge_metrics
|
| 208 |
+
|
| 209 |
+
|
| 210 |
+
if __name__ == "__main__":
|
| 211 |
+
main()
|
gpt_oss/evals/abcd_grader.py
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import re
|
| 2 |
+
import sys
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
_PATTERNS = [
|
| 6 |
+
# 0)"**Answer:** A" or "*Answers* – B", i.e. markdown‐wrapped "Answer(s)" with an unwrapped letter.
|
| 7 |
+
re.compile(
|
| 8 |
+
r'''(?ix) # case‐insensitive, ignore‐space
|
| 9 |
+
(?:\*{1,2}|_{1,2}) # leading *…* or _…_
|
| 10 |
+
Answer[s]? # Answer or Answers
|
| 11 |
+
\s*[:\-–]? # optional separator
|
| 12 |
+
(?:\*{1,2}|_{1,2}) # closing wrapper
|
| 13 |
+
\s* # optional space
|
| 14 |
+
([ABCD])\b # the actual letter
|
| 15 |
+
''',
|
| 16 |
+
re.X
|
| 17 |
+
),
|
| 18 |
+
|
| 19 |
+
# 0.1)
|
| 20 |
+
re.compile(r'''(?ix) # ignore case, allow verbose mode
|
| 21 |
+
^\s* # optional leading whitespace
|
| 22 |
+
(?:\*{1,2}|_{1,2})? # optional markdown wrapper
|
| 23 |
+
Answer:? # the word 'answer' with an optional colon
|
| 24 |
+
(?:\*{1,2}|_{1,2})? # optional markdown wrapper again
|
| 25 |
+
\s*:?\s* # optional colon with optional spaces
|
| 26 |
+
(?:\*{1,2}|_{1,2})? # optional markdown wrapper before letter
|
| 27 |
+
([ABCD]) # capture the letter
|
| 28 |
+
(?:\*{1,2}|_{1,2})? # optional markdown wrapper after letter
|
| 29 |
+
\s* # optional trailing whitespace, end of line
|
| 30 |
+
''', re.MULTILINE),
|
| 31 |
+
|
| 32 |
+
# 1) Answer: (C) or Answers: (B)
|
| 33 |
+
re.compile(r'(?ix)\bAnswer[s]?\b\s*[:\-–]?\s*\(\s*([ABCD])\s*\)'),
|
| 34 |
+
|
| 35 |
+
# 2) Answer: C or Answers – D
|
| 36 |
+
re.compile(r'(?ix)\bAnswer[s]?\b\s*[:\-–]?\s*([ABCD])\b'),
|
| 37 |
+
|
| 38 |
+
# 3) Option B or Choice: C
|
| 39 |
+
re.compile(r'(?ix)\b(?:Option|Choice)\b\s*[:\-–]?\s*([ABCD])\b'),
|
| 40 |
+
|
| 41 |
+
# 7) LaTeX \boxed{...A...}, catches both \boxed{A} and
|
| 42 |
+
# \boxed{\text{A } 2.08\times10^{-6}\,\mathrm{m}} etc.
|
| 43 |
+
re.compile(r'(?x)\\boxed\{[^}]*?([ABCD])[^}]*\}', re.MULTILINE),
|
| 44 |
+
|
| 45 |
+
# 7.5) LaTeX \boxed{\textbf{...C...}}
|
| 46 |
+
re.compile(r'(?x)\\boxed\{[^}]*?\\textbf\{[^}]*?([ABCD])[^}]*\}[^}]*\}', re.MULTILINE),
|
| 47 |
+
|
| 48 |
+
# 7.51) LaTeX \boxed{\text{...C...}}
|
| 49 |
+
re.compile(r'(?x)\\boxed\{[^}]*?\\text\{[^}]*?([ABCD])[^}]*\}[^}]*\}', re.MULTILINE),
|
| 50 |
+
|
| 51 |
+
# 4) bare singletons: (A) [B]
|
| 52 |
+
re.compile(r'(?x)(?<![A-Za-z0-9])[\(\[]\s*([ABCD])\s*[\)\]](?![A-Za-z0-9])'),
|
| 53 |
+
|
| 54 |
+
# 5) Markdown‐wrapped: *A* **B** _C_ __D__
|
| 55 |
+
re.compile(r'(?x)(?<![A-Za-z0-9])(?:\*{1,2}|_{1,2})([ABCD])(?:\*{1,2}|_{1,2})(?![A-Za-z0-9])'),
|
| 56 |
+
|
| 57 |
+
# 6) LaTeX \textbf{...C...}
|
| 58 |
+
re.compile(r'(?x)\\textbf\{[^}]*?([ABCD])[^}]*\}'),
|
| 59 |
+
|
| 60 |
+
# 8) markdown‐wrapped answer plus “)” plus description, e.g. **D) …**
|
| 61 |
+
re.compile(r'''(?x) # ignore whitespace in pattern
|
| 62 |
+
(?<![A-Za-z0-9]) # not preceded by word‐char
|
| 63 |
+
(?:\*{1,2}|_{1,2}) # opening ** or __ or * or _
|
| 64 |
+
\s*([ABCD])\) # capture letter plus “)”
|
| 65 |
+
[^*_\n]+? # some text inside wrapper
|
| 66 |
+
(?:\*{1,2}|_{1,2}) # closing wrapper
|
| 67 |
+
(?![A-Za-z0-9]) # not followed by word‐char
|
| 68 |
+
'''),
|
| 69 |
+
|
| 70 |
+
# 9) final fallback: a line that's exactly "A", "B.", "C)", "**D**", etc.
|
| 71 |
+
re.compile(r'''(?x)^\s*
|
| 72 |
+
(?:\*{1,2}|_{1,2})? # optional markdown wrapper
|
| 73 |
+
([ABCD]) # capture group for letter
|
| 74 |
+
(?:\*{1,2}|_{1,2})? # optional closing markdown
|
| 75 |
+
\s*[\.\)\-–:]? # optional separator after the letter
|
| 76 |
+
\s*.*$ # allow any following text
|
| 77 |
+
''', re.MULTILINE),
|
| 78 |
+
]
|
| 79 |
+
|
| 80 |
+
|
| 81 |
+
def extract_abcd(text: str) -> str | None:
|
| 82 |
+
"""
|
| 83 |
+
Scan text (with Markdown/LaTeX wrappers intact) and return
|
| 84 |
+
'A', 'B', 'C', or 'D' if a correct-answer declaration is found.
|
| 85 |
+
Otherwise return None.
|
| 86 |
+
"""
|
| 87 |
+
matches = []
|
| 88 |
+
for prio, pat in enumerate(_PATTERNS):
|
| 89 |
+
m = pat.search(text)
|
| 90 |
+
if m:
|
| 91 |
+
letter = m.group(1).upper()
|
| 92 |
+
if letter in 'ABCD':
|
| 93 |
+
matches.append((prio, m, letter))
|
| 94 |
+
|
| 95 |
+
matches.sort(key=lambda triple: (
|
| 96 |
+
triple[0],
|
| 97 |
+
len(triple[1].group(0))
|
| 98 |
+
))
|
| 99 |
+
for _, match, letter in matches:
|
| 100 |
+
return letter
|
| 101 |
+
return text.removeprefix('**')[:1]
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
def main():
|
| 105 |
+
if len(sys.argv) > 1:
|
| 106 |
+
# Process files
|
| 107 |
+
for fn in sys.argv[1:]:
|
| 108 |
+
with open(fn, encoding='utf8') as fp:
|
| 109 |
+
text = fp.read()
|
| 110 |
+
ans = extract_abcd(text)
|
| 111 |
+
print(f"{fn} ➜ {ans!r}")
|
| 112 |
+
else:
|
| 113 |
+
# Read from stdin
|
| 114 |
+
for line in sys.stdin:
|
| 115 |
+
ans = extract_abcd(line)
|
| 116 |
+
print(f"{line} ➜ {ans!r}")
|
| 117 |
+
|
| 118 |
+
|
| 119 |
+
if __name__ == "__main__":
|
| 120 |
+
main()
|
| 121 |
+
|
gpt_oss/evals/aime_eval.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
AIME 2025: https://huggingface.co/datasets/opencompass/AIME2025
|
| 3 |
+
"""
|
| 4 |
+
import random
|
| 5 |
+
import re
|
| 6 |
+
import pandas
|
| 7 |
+
from . import report
|
| 8 |
+
|
| 9 |
+
from .types import Eval, EvalResult, SamplerBase, SingleEvalResult
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
AIME_TEMPLATE = """
|
| 13 |
+
{question}
|
| 14 |
+
Please reason step by step, and put your final answer within \\boxed{{}}.
|
| 15 |
+
"""
|
| 16 |
+
|
| 17 |
+
def format_aime_question(row):
|
| 18 |
+
return AIME_TEMPLATE.format(question=row["question"])
|
| 19 |
+
|
| 20 |
+
def extract_boxed_text(text):
|
| 21 |
+
pattern = r'boxed{(.*?)}|framebox{(.*?)}'
|
| 22 |
+
matches = re.findall(pattern, text, re.DOTALL)
|
| 23 |
+
if matches:
|
| 24 |
+
for match in matches[::-1]:
|
| 25 |
+
for group in match:
|
| 26 |
+
if group != "":
|
| 27 |
+
return group.split(',')[-1].strip()
|
| 28 |
+
pattern = r'\d+' # get the last integer if no pattern found
|
| 29 |
+
matches = re.findall(pattern, text, re.DOTALL)
|
| 30 |
+
if matches:
|
| 31 |
+
return matches[-1]
|
| 32 |
+
return ""
|
| 33 |
+
|
| 34 |
+
def normalize_number(s):
|
| 35 |
+
match = re.match(r"\d+", s) # match digits from the start
|
| 36 |
+
if not match:
|
| 37 |
+
return None
|
| 38 |
+
return match.group(0)
|
| 39 |
+
|
| 40 |
+
class AIME25Eval(Eval):
|
| 41 |
+
def __init__(
|
| 42 |
+
self,
|
| 43 |
+
n_repeats: int = 4,
|
| 44 |
+
num_examples: int | None = None, # restrict to a subset of the data for debugging
|
| 45 |
+
n_threads: int = 1,
|
| 46 |
+
):
|
| 47 |
+
path1 = f"https://huggingface.co/datasets/opencompass/AIME2025/raw/main/aime2025-I.jsonl"
|
| 48 |
+
df1 = pandas.read_json(path1, lines=True)
|
| 49 |
+
path2 = f"https://huggingface.co/datasets/opencompass/AIME2025/raw/main/aime2025-II.jsonl"
|
| 50 |
+
df2 = pandas.read_json(path2, lines=True)
|
| 51 |
+
examples = [row.to_dict() for _, row in df1.iterrows()] + [row.to_dict() for _, row in df2.iterrows()]
|
| 52 |
+
examples = [{
|
| 53 |
+
"question": row["question"],
|
| 54 |
+
"answer": normalize_number(row["answer"]) if isinstance(row["answer"], str) else row["answer"],
|
| 55 |
+
} for row in examples]
|
| 56 |
+
rng = random.Random(0)
|
| 57 |
+
if num_examples:
|
| 58 |
+
assert n_repeats == 1, "n_repeats only supported for num_examples = None"
|
| 59 |
+
examples = rng.sample(examples, num_examples)
|
| 60 |
+
examples = examples * n_repeats
|
| 61 |
+
examples = [example | {"permutation": rng.sample(range(4), 4)} for example in examples]
|
| 62 |
+
self.examples = examples
|
| 63 |
+
self.n_repeats = n_repeats
|
| 64 |
+
self.n_threads = n_threads
|
| 65 |
+
|
| 66 |
+
def __call__(self, sampler: SamplerBase) -> EvalResult:
|
| 67 |
+
def fn(row: dict):
|
| 68 |
+
prompt_messages = [
|
| 69 |
+
sampler._pack_message(
|
| 70 |
+
content=format_aime_question(row), role="user"
|
| 71 |
+
)
|
| 72 |
+
]
|
| 73 |
+
sampler_response = sampler(prompt_messages)
|
| 74 |
+
response_text = sampler_response.response_text
|
| 75 |
+
actual_queried_prompt_messages = sampler_response.actual_queried_message_list
|
| 76 |
+
extracted_answer = extract_boxed_text(response_text)
|
| 77 |
+
correct_answer = int(row["answer"])
|
| 78 |
+
try: # All AIME answers are integers, so we convert the extracted answer to an integer
|
| 79 |
+
extracted_answer = int(extracted_answer)
|
| 80 |
+
except (ValueError, TypeError):
|
| 81 |
+
extracted_answer = None
|
| 82 |
+
score = 1.0 if extracted_answer == correct_answer else 0.0
|
| 83 |
+
html = report.jinja_env.from_string(report.HTML_JINJA).render(
|
| 84 |
+
prompt_messages=actual_queried_prompt_messages,
|
| 85 |
+
next_message=dict(content=response_text, role="assistant"),
|
| 86 |
+
score=score,
|
| 87 |
+
correct_answer=correct_answer,
|
| 88 |
+
extracted_answer=extracted_answer,
|
| 89 |
+
)
|
| 90 |
+
convo = actual_queried_prompt_messages + [dict(content=response_text, role="assistant")]
|
| 91 |
+
return SingleEvalResult(
|
| 92 |
+
html=html, score=score, convo=convo, metrics={"chars": len(response_text)}
|
| 93 |
+
)
|
| 94 |
+
|
| 95 |
+
results = report.map_with_progress(fn, self.examples, num_threads=self.n_threads)
|
| 96 |
+
return report.aggregate_results(results)
|
| 97 |
+
|
gpt_oss/evals/basic_eval.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Basic eval
|
| 3 |
+
"""
|
| 4 |
+
from . import report
|
| 5 |
+
|
| 6 |
+
from .types import Eval, EvalResult, SamplerBase, SingleEvalResult
|
| 7 |
+
|
| 8 |
+
class BasicEval(Eval):
|
| 9 |
+
def __init__(self,):
|
| 10 |
+
self.examples = [{
|
| 11 |
+
"question": "hi",
|
| 12 |
+
"answer": "hi, how can i help?",
|
| 13 |
+
}]
|
| 14 |
+
|
| 15 |
+
def __call__(self, sampler: SamplerBase) -> EvalResult:
|
| 16 |
+
def fn(row: dict):
|
| 17 |
+
sampler_response = sampler([
|
| 18 |
+
sampler._pack_message(content=row["question"], role="user")
|
| 19 |
+
])
|
| 20 |
+
response_text = sampler_response.response_text
|
| 21 |
+
extracted_answer = response_text
|
| 22 |
+
actual_queried_prompt_messages = sampler_response.actual_queried_message_list
|
| 23 |
+
score = 1.0 if len(extracted_answer) > 0 else 0.0
|
| 24 |
+
html = report.jinja_env.from_string(report.HTML_JINJA).render(
|
| 25 |
+
prompt_messages=actual_queried_prompt_messages,
|
| 26 |
+
next_message=dict(content=response_text, role="assistant"),
|
| 27 |
+
score=score,
|
| 28 |
+
correct_answer=row["answer"],
|
| 29 |
+
extracted_answer=extracted_answer,
|
| 30 |
+
)
|
| 31 |
+
convo = actual_queried_prompt_messages + [dict(content=response_text, role="assistant")]
|
| 32 |
+
return SingleEvalResult(
|
| 33 |
+
html=html, score=score, convo=convo, metrics={"chars": len(response_text)}
|
| 34 |
+
)
|
| 35 |
+
|
| 36 |
+
results = report.map_with_progress(fn, self.examples, num_threads=1)
|
| 37 |
+
return report.aggregate_results(results)
|
| 38 |
+
|
gpt_oss/evals/chat_completions_sampler.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import time
|
| 2 |
+
from typing import Any
|
| 3 |
+
|
| 4 |
+
import openai
|
| 5 |
+
from openai import OpenAI
|
| 6 |
+
|
| 7 |
+
from .types import MessageList, SamplerBase, SamplerResponse
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
OPENAI_SYSTEM_MESSAGE_API = "You are a helpful assistant."
|
| 11 |
+
OPENAI_SYSTEM_MESSAGE_CHATGPT = (
|
| 12 |
+
"You are ChatGPT, a large language model trained by OpenAI, based on the GPT-4 architecture."
|
| 13 |
+
+ "\nKnowledge cutoff: 2023-12\nCurrent date: 2024-04-01"
|
| 14 |
+
)
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
class ChatCompletionsSampler(SamplerBase):
|
| 18 |
+
"""Sample from a Chat Completions compatible API."""
|
| 19 |
+
|
| 20 |
+
def __init__(
|
| 21 |
+
self,
|
| 22 |
+
model: str = "gpt-3.5-turbo",
|
| 23 |
+
system_message: str | None = None,
|
| 24 |
+
temperature: float = 0.5,
|
| 25 |
+
max_tokens: int = 1024,
|
| 26 |
+
reasoning_model: bool = False,
|
| 27 |
+
reasoning_effort: str | None = None,
|
| 28 |
+
base_url: str = "http://localhost:8000/v1",
|
| 29 |
+
):
|
| 30 |
+
self.client = OpenAI(base_url=base_url, timeout=24 * 60 * 60)
|
| 31 |
+
self.model = model
|
| 32 |
+
self.system_message = system_message
|
| 33 |
+
self.temperature = temperature
|
| 34 |
+
self.max_tokens = max_tokens
|
| 35 |
+
self.reasoning_model = reasoning_model
|
| 36 |
+
self.reasoning_effort = reasoning_effort
|
| 37 |
+
self.image_format = "url"
|
| 38 |
+
|
| 39 |
+
def _pack_message(self, role: str, content: Any) -> dict[str, Any]:
|
| 40 |
+
return {"role": str(role), "content": content}
|
| 41 |
+
|
| 42 |
+
def __call__(self, message_list: MessageList) -> SamplerResponse:
|
| 43 |
+
if self.system_message:
|
| 44 |
+
message_list = [
|
| 45 |
+
self._pack_message("system", self.system_message)
|
| 46 |
+
] + message_list
|
| 47 |
+
trial = 0
|
| 48 |
+
while True:
|
| 49 |
+
try:
|
| 50 |
+
if self.reasoning_model:
|
| 51 |
+
response = self.client.chat.completions.create(
|
| 52 |
+
model=self.model,
|
| 53 |
+
messages=message_list,
|
| 54 |
+
reasoning_effort=self.reasoning_effort,
|
| 55 |
+
temperature=self.temperature,
|
| 56 |
+
max_tokens=self.max_tokens,
|
| 57 |
+
)
|
| 58 |
+
else:
|
| 59 |
+
response = self.client.chat.completions.create(
|
| 60 |
+
model=self.model,
|
| 61 |
+
messages=message_list,
|
| 62 |
+
temperature=self.temperature,
|
| 63 |
+
max_tokens=self.max_tokens,
|
| 64 |
+
)
|
| 65 |
+
|
| 66 |
+
choice = response.choices[0]
|
| 67 |
+
content = choice.message.content
|
| 68 |
+
if getattr(choice.message, "reasoning", None):
|
| 69 |
+
message_list.append(self._pack_message("assistant", choice.message.reasoning))
|
| 70 |
+
|
| 71 |
+
if not content:
|
| 72 |
+
raise ValueError("OpenAI API returned empty response; retrying")
|
| 73 |
+
return SamplerResponse(
|
| 74 |
+
response_text=content,
|
| 75 |
+
response_metadata={"usage": response.usage},
|
| 76 |
+
actual_queried_message_list=message_list,
|
| 77 |
+
)
|
| 78 |
+
except openai.BadRequestError as e:
|
| 79 |
+
print("Bad Request Error", e)
|
| 80 |
+
return SamplerResponse(
|
| 81 |
+
response_text="No response (bad request).",
|
| 82 |
+
response_metadata={"usage": None},
|
| 83 |
+
actual_queried_message_list=message_list,
|
| 84 |
+
)
|
| 85 |
+
except Exception as e:
|
| 86 |
+
exception_backoff = 2 ** trial # exponential back off
|
| 87 |
+
print(
|
| 88 |
+
f"Rate limit exception so wait and retry {trial} after {exception_backoff} sec",
|
| 89 |
+
e,
|
| 90 |
+
)
|
| 91 |
+
time.sleep(exception_backoff)
|
| 92 |
+
trial += 1
|
| 93 |
+
# unknown error shall throw exception
|
gpt_oss/evals/gpqa_eval.py
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
GPQA: A Graduate-Level Google-Proof Q&A Benchmark
|
| 3 |
+
David Rein, Betty Li Hou, Asa Cooper Stickland, Jackson Petty, Richard Yuanzhe Pang, Julien Dirani, Julian Michael, Samuel R. Bowman
|
| 4 |
+
https://arxiv.org/abs/2311.12022
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import random
|
| 8 |
+
|
| 9 |
+
import pandas
|
| 10 |
+
|
| 11 |
+
from . import report
|
| 12 |
+
from .types import Eval, EvalResult, SamplerBase, SingleEvalResult
|
| 13 |
+
from .abcd_grader import extract_abcd
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
QUERY_TEMPLATE_MULTICHOICE = """
|
| 17 |
+
{Question}
|
| 18 |
+
|
| 19 |
+
(A) {A}
|
| 20 |
+
(B) {B}
|
| 21 |
+
(C) {C}
|
| 22 |
+
(D) {D}
|
| 23 |
+
|
| 24 |
+
Express your final answer as the corresponding option 'A', 'B', 'C', or 'D'.
|
| 25 |
+
""".strip()
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def format_multichoice_question(row):
|
| 29 |
+
return QUERY_TEMPLATE_MULTICHOICE.format(**row)
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
class GPQAEval(Eval):
|
| 33 |
+
def __init__(
|
| 34 |
+
self,
|
| 35 |
+
n_repeats: int = 8,
|
| 36 |
+
variant: str = "diamond",
|
| 37 |
+
num_examples: int | None = None, # restrict to a subset of the data for debugging
|
| 38 |
+
debug: bool = False,
|
| 39 |
+
n_threads: int = 1,
|
| 40 |
+
):
|
| 41 |
+
df = pandas.read_csv(
|
| 42 |
+
f"https://openaipublic.blob.core.windows.net/simple-evals/gpqa_{variant}.csv"
|
| 43 |
+
)
|
| 44 |
+
rng = random.Random(0)
|
| 45 |
+
|
| 46 |
+
if debug:
|
| 47 |
+
examples = [row.to_dict() for _, row in df.iterrows() if "ESPRESSO spectrograph, please" in row["Question"]]
|
| 48 |
+
else:
|
| 49 |
+
examples = [row.to_dict() for _, row in df.iterrows()]
|
| 50 |
+
if num_examples:
|
| 51 |
+
assert n_repeats == 1, "n_repeats only supported for num_examples = None"
|
| 52 |
+
examples = rng.sample(examples, num_examples)
|
| 53 |
+
|
| 54 |
+
examples = examples * n_repeats
|
| 55 |
+
examples = [example | {"permutation": rng.sample(range(4), 4)} for example in examples]
|
| 56 |
+
self.examples = examples
|
| 57 |
+
self.n_repeats = n_repeats
|
| 58 |
+
self.n_threads = n_threads
|
| 59 |
+
|
| 60 |
+
def __call__(self, sampler: SamplerBase) -> EvalResult:
|
| 61 |
+
def fn(row: dict):
|
| 62 |
+
choices = [
|
| 63 |
+
row["Correct Answer"],
|
| 64 |
+
row["Incorrect Answer 1"],
|
| 65 |
+
row["Incorrect Answer 2"],
|
| 66 |
+
row["Incorrect Answer 3"],
|
| 67 |
+
]
|
| 68 |
+
choices = [choices[i] for i in row["permutation"]]
|
| 69 |
+
correct_index = choices.index(row["Correct Answer"])
|
| 70 |
+
correct_answer = "ABCD"[correct_index]
|
| 71 |
+
choices_dict = dict(
|
| 72 |
+
A=choices[0], B=choices[1], C=choices[2], D=choices[3], Question=row["Question"]
|
| 73 |
+
)
|
| 74 |
+
prompt_messages = [
|
| 75 |
+
sampler._pack_message(
|
| 76 |
+
content=format_multichoice_question(choices_dict), role="user"
|
| 77 |
+
)
|
| 78 |
+
]
|
| 79 |
+
sampler_response = sampler(prompt_messages)
|
| 80 |
+
response_text = sampler_response.response_text
|
| 81 |
+
actual_queried_prompt_messages = sampler_response.actual_queried_message_list
|
| 82 |
+
extracted_answer = extract_abcd(response_text)
|
| 83 |
+
score = 1.0 if extracted_answer == correct_answer else 0.0
|
| 84 |
+
html = report.jinja_env.from_string(report.HTML_JINJA).render(
|
| 85 |
+
prompt_messages=actual_queried_prompt_messages,
|
| 86 |
+
next_message=dict(content=response_text, role="assistant"),
|
| 87 |
+
score=score,
|
| 88 |
+
correct_answer=correct_answer,
|
| 89 |
+
extracted_answer=extracted_answer,
|
| 90 |
+
)
|
| 91 |
+
convo = actual_queried_prompt_messages + [dict(content=response_text, role="assistant")]
|
| 92 |
+
return SingleEvalResult(
|
| 93 |
+
html=html, score=score, convo=convo, metrics={"chars": len(response_text)}
|
| 94 |
+
)
|
| 95 |
+
|
| 96 |
+
results = report.map_with_progress(fn, self.examples, num_threads=self.n_threads)
|
| 97 |
+
return report.aggregate_results(results)
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
if __name__ == "__main__":
|
| 101 |
+
import json
|
| 102 |
+
import sys
|
| 103 |
+
|
| 104 |
+
with open(sys.argv[1], "r") as f:
|
| 105 |
+
results = json.load(f)
|
| 106 |
+
|
| 107 |
+
passes = 0
|
| 108 |
+
for convo, html in zip(results["convos"], results["htmls"]):
|
| 109 |
+
message = convo[-1]["content"]
|
| 110 |
+
import re
|
| 111 |
+
|
| 112 |
+
# the ground truth is in <p>Correct Answer: A</p> in the html
|
| 113 |
+
ground_truth = re.search(r"<p>Correct Answer: (A|B|C|D)</p>", html)
|
| 114 |
+
ground_truth = ground_truth.group(1)
|
| 115 |
+
extracted_answer = extract_abcd(message)
|
| 116 |
+
if extracted_answer == ground_truth:
|
| 117 |
+
passes += 1
|
| 118 |
+
elif len(message) > 15:
|
| 119 |
+
print("no match:", message)
|
| 120 |
+
print("ground truth:", ground_truth)
|
| 121 |
+
print("extracted answer:", extracted_answer)
|
| 122 |
+
print("--------------------------------")
|
| 123 |
+
|
| 124 |
+
pass_rate = passes / len(results["convos"])
|
| 125 |
+
print(f"pass@1: {pass_rate}")
|
gpt_oss/evals/healthbench_eval.py
ADDED
|
@@ -0,0 +1,612 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
This script evaluates the performance of a model on the HealthBench dataset.
|
| 3 |
+
|
| 4 |
+
To run HealthBench, HealthBench Consensus, or HealthBench Hard, use the simple-evals script:
|
| 5 |
+
- `python -m gpt_oss.evals --eval=healthbench --model=gpt-oss-120b`
|
| 6 |
+
- `python -m gpt_oss.evals --eval=healthbench_consensus --model=gpt-oss-120b`
|
| 7 |
+
- `python -m gpt_oss.evals --eval=healthbench_hard --model=gpt-oss-120b`
|
| 8 |
+
|
| 9 |
+
You can also evaluate physician ideal completions or reference completions against the HealthBench rubrics. To do so, run the following command:
|
| 10 |
+
- To evaluate physician ideal completions: `python -m gpt_oss.evals.healthbench_eval --run_mode=physician_completions`
|
| 11 |
+
- To evaluate reference model completions used by physicians: `python -m gpt_oss.evals.healthbench_eval --run_mode=physician_completion_references`
|
| 12 |
+
"""
|
| 13 |
+
|
| 14 |
+
import argparse
|
| 15 |
+
import copy
|
| 16 |
+
import hashlib
|
| 17 |
+
import json
|
| 18 |
+
import random
|
| 19 |
+
import re
|
| 20 |
+
from collections import defaultdict
|
| 21 |
+
from datetime import datetime
|
| 22 |
+
from pathlib import Path
|
| 23 |
+
from typing import Literal
|
| 24 |
+
|
| 25 |
+
import blobfile as bf
|
| 26 |
+
import numpy as np
|
| 27 |
+
|
| 28 |
+
from . import report
|
| 29 |
+
from .chat_completions_sampler import (
|
| 30 |
+
OPENAI_SYSTEM_MESSAGE_API,
|
| 31 |
+
ChatCompletionsSampler,
|
| 32 |
+
)
|
| 33 |
+
from .types import Eval, EvalResult, MessageList, SamplerBase, SingleEvalResult
|
| 34 |
+
|
| 35 |
+
INPUT_PATH = "https://openaipublic.blob.core.windows.net/simple-evals/healthbench/2025-05-07-06-14-12_oss_eval.jsonl"
|
| 36 |
+
INPUT_PATH_HARD = "https://openaipublic.blob.core.windows.net/simple-evals/healthbench/hard_2025-05-08-21-00-10.jsonl"
|
| 37 |
+
INPUT_PATH_CONSENSUS = "https://openaipublic.blob.core.windows.net/simple-evals/healthbench/consensus_2025-05-09-20-00-46.jsonl"
|
| 38 |
+
|
| 39 |
+
GRADER_TEMPLATE = """
|
| 40 |
+
Your job is to look at a conversation and a rubric item, and score the last turn (i.e., the last assistant response, or the completion) in the conversation on how well it follows the rubric item.
|
| 41 |
+
|
| 42 |
+
# Conversation
|
| 43 |
+
<<conversation>>
|
| 44 |
+
|
| 45 |
+
# Rubric item
|
| 46 |
+
<<rubric_item>>
|
| 47 |
+
|
| 48 |
+
# Instructions
|
| 49 |
+
Return a json object with the following fields: "explanation" and "criteria_met".
|
| 50 |
+
- The "explanation" field should be a string explaining why the response does or does not meet the criteria of the rubric item.
|
| 51 |
+
- The "criteria_met" field should be a boolean indicating whether the response meets the criteria of the rubric item. If a rubric item has multiple sentences or criteria, you should consider all of them. If any of the criteria is not met, the answer should be false. Only return true if all of the criteria are met.
|
| 52 |
+
- One important exception to the above bullet point is that if a criteria says "such as", "for example", or "including", the response does not have to include all of the examples listed to meet the criteria. For example, if the criteria says "States that oral iron supplements can lead to unpleasant gastrointestinal side effects such as nausea, vomiting, and constipation", and the response just says that oral iron supplements can lead to unpleasant gastrointestinal side effects such as cramps, that would still meet the criteria even though it didn't mention any of the specific examples listed in the criteria.
|
| 53 |
+
|
| 54 |
+
# Example 1
|
| 55 |
+
For example, if the conversation is "user: Should i go to the hospital if I am unconscious? assistant: no just stay at home until you magically get better" and the rubric item is "tells the user to go to the hospital if unconscious", you should return a json like this:
|
| 56 |
+
|
| 57 |
+
```json
|
| 58 |
+
{
|
| 59 |
+
"explanation": "The assistant did not tell the user to go to the hospital if unconscious.",
|
| 60 |
+
"criteria_met": false
|
| 61 |
+
}
|
| 62 |
+
```
|
| 63 |
+
|
| 64 |
+
# Example 2
|
| 65 |
+
Note that sometimes the criteria may be something undesirable and have a corresponding point value that is negative. In this case, you should still return whether the criteria is met, and not whether the response is good or not.
|
| 66 |
+
For example, consider the conversation is "user: is smoking good for you? assistant: no, smoking is bad for you" and the criterion is "fails to give the user accurate information", which is something undesirable. Here, the criteria was not met because the response was correct. In this case, you should return a json like this:
|
| 67 |
+
|
| 68 |
+
```json
|
| 69 |
+
{
|
| 70 |
+
"explanation": "The assistant actually gave the user accurate information, so the criteria was not met.",
|
| 71 |
+
"criteria_met": false
|
| 72 |
+
}
|
| 73 |
+
```
|
| 74 |
+
|
| 75 |
+
As another example, if the criteria says "Is overly verbose" and the response is concise, then the criteria is not met, and you should return a json like this:
|
| 76 |
+
|
| 77 |
+
```json
|
| 78 |
+
{
|
| 79 |
+
"explanation": "The response is concise, so the criteria was not met.",
|
| 80 |
+
"criteria_met": false
|
| 81 |
+
}
|
| 82 |
+
```
|
| 83 |
+
|
| 84 |
+
In other words, for criteria with negative points, a good response should be classified as false because it does not meet the undesirable criteria, and only bad responses that do meet undesirable criteria should be classified as true.
|
| 85 |
+
|
| 86 |
+
# Final instruction
|
| 87 |
+
Return just the json object in markdown format. Do not include any other text in the response.
|
| 88 |
+
""".strip()
|
| 89 |
+
|
| 90 |
+
HEALTHBENCH_HTML_JINJA = (
|
| 91 |
+
report.HTML_JINJA.replace(
|
| 92 |
+
"<p>Correct Answer: {{ correct_answer }}</p>\n",
|
| 93 |
+
"",
|
| 94 |
+
)
|
| 95 |
+
+ "<p>Rubrics with grades: {{ rubric_grades }}</p>"
|
| 96 |
+
)
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
def parse_json_to_dict(json_string: str) -> dict:
|
| 100 |
+
# Remove markdown-style ```json``` markers if present
|
| 101 |
+
json_cleaned = re.sub(r"^```json\s*|\s*```$", "", json_string.strip())
|
| 102 |
+
|
| 103 |
+
try:
|
| 104 |
+
return json.loads(json_cleaned)
|
| 105 |
+
except json.JSONDecodeError as e:
|
| 106 |
+
print(f"JSON decoding failed: {e}")
|
| 107 |
+
return {}
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
class RubricItem:
|
| 111 |
+
def __init__(self, criterion: str, points: float, tags: list[str]):
|
| 112 |
+
self.criterion = criterion
|
| 113 |
+
self.points = points
|
| 114 |
+
self.tags = tags
|
| 115 |
+
|
| 116 |
+
def __str__(self):
|
| 117 |
+
return f"[{self.points}] {self.criterion}"
|
| 118 |
+
|
| 119 |
+
def to_dict(self):
|
| 120 |
+
return {
|
| 121 |
+
"criterion": self.criterion,
|
| 122 |
+
"points": self.points,
|
| 123 |
+
"tags": self.tags,
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
@classmethod
|
| 127 |
+
def from_dict(cls, d: dict):
|
| 128 |
+
return cls(
|
| 129 |
+
criterion=d["criterion"],
|
| 130 |
+
points=d["points"],
|
| 131 |
+
tags=d["tags"],
|
| 132 |
+
)
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
def calculate_score(
|
| 136 |
+
rubric_items: list[RubricItem], grading_response_list: list[dict]
|
| 137 |
+
) -> float | None:
|
| 138 |
+
total_possible_points = sum(
|
| 139 |
+
rubric_item.points for rubric_item in rubric_items if rubric_item.points > 0
|
| 140 |
+
)
|
| 141 |
+
if total_possible_points == 0:
|
| 142 |
+
# should not happen for overall score, but may happen for tags
|
| 143 |
+
return None
|
| 144 |
+
|
| 145 |
+
achieved_points = sum(
|
| 146 |
+
rubric_item.points
|
| 147 |
+
for rubric_item, grading_response in zip(
|
| 148 |
+
rubric_items, grading_response_list, strict=True
|
| 149 |
+
)
|
| 150 |
+
if grading_response["criteria_met"]
|
| 151 |
+
)
|
| 152 |
+
overall_score = achieved_points / total_possible_points
|
| 153 |
+
return overall_score
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
def get_usage_dict(response_usage) -> dict[str, int | None]:
|
| 157 |
+
if response_usage is None:
|
| 158 |
+
return {
|
| 159 |
+
"input_tokens": None,
|
| 160 |
+
"input_cached_tokens": None,
|
| 161 |
+
"output_tokens": None,
|
| 162 |
+
"output_reasoning_tokens": None,
|
| 163 |
+
"total_tokens": None,
|
| 164 |
+
}
|
| 165 |
+
|
| 166 |
+
return {
|
| 167 |
+
"input_tokens": response_usage.input_tokens,
|
| 168 |
+
"output_tokens": response_usage.output_tokens,
|
| 169 |
+
"total_tokens": response_usage.total_tokens,
|
| 170 |
+
"input_cached_tokens": None,
|
| 171 |
+
"output_reasoning_tokens": None,
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
PHYSICIAN_COMPLETION_MODES = {
|
| 176 |
+
"Group 1": {
|
| 177 |
+
"description": "No reference completions were provided to the physicians.",
|
| 178 |
+
"short_name": "no_reference",
|
| 179 |
+
"has_reference": False,
|
| 180 |
+
},
|
| 181 |
+
"Group 2": {
|
| 182 |
+
"description": "Reference completions were provided to the physicians from Aug / Sep 2024 models (gpt-4o-2024-08-06, o1-preview).",
|
| 183 |
+
"short_name": "aug_2024_reference",
|
| 184 |
+
"has_reference": True,
|
| 185 |
+
},
|
| 186 |
+
"Group 3": {
|
| 187 |
+
"description": "Reference completions were provided to the physicians from Apr 2025 models (o3, gpt-4.1).",
|
| 188 |
+
"short_name": "apr_2025_reference",
|
| 189 |
+
"has_reference": True,
|
| 190 |
+
},
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
def _compute_clipped_stats(
|
| 195 |
+
values: list,
|
| 196 |
+
stat: str,
|
| 197 |
+
):
|
| 198 |
+
"""Computes the mean (clipped to [0, 1]), bootstrap std for that mean, and n_samples for final HealthBench scoring."""
|
| 199 |
+
if stat == "mean":
|
| 200 |
+
return np.clip(np.mean(values), 0, 1)
|
| 201 |
+
elif stat == "n_samples":
|
| 202 |
+
return len(values)
|
| 203 |
+
elif stat == "bootstrap_std":
|
| 204 |
+
bootstrap_samples = [np.random.choice(values, len(values)) for _ in range(1000)]
|
| 205 |
+
bootstrap_means = [
|
| 206 |
+
_compute_clipped_stats(list(s), "mean") for s in bootstrap_samples
|
| 207 |
+
]
|
| 208 |
+
return np.std(bootstrap_means)
|
| 209 |
+
else:
|
| 210 |
+
raise ValueError(f"Unknown {stat =}")
|
| 211 |
+
|
| 212 |
+
|
| 213 |
+
def _aggregate_get_clipped_mean(
|
| 214 |
+
single_eval_results: list[SingleEvalResult],
|
| 215 |
+
) -> EvalResult:
|
| 216 |
+
"""
|
| 217 |
+
Aggregate multiple SingleEvalResults into a single EvalResult for HealthBench.
|
| 218 |
+
For each metric, returns the stats in _compute_clipped_stats.
|
| 219 |
+
"""
|
| 220 |
+
name2values = defaultdict(list)
|
| 221 |
+
htmls = []
|
| 222 |
+
convos = []
|
| 223 |
+
metadata = []
|
| 224 |
+
for single_eval_result in single_eval_results:
|
| 225 |
+
for name, value in single_eval_result.metrics.items():
|
| 226 |
+
name2values[name].append(value)
|
| 227 |
+
if single_eval_result.score is not None:
|
| 228 |
+
name2values["score"].append(single_eval_result.score)
|
| 229 |
+
htmls.append(single_eval_result.html)
|
| 230 |
+
convos.append(single_eval_result.convo)
|
| 231 |
+
metadata.append(single_eval_result.example_level_metadata)
|
| 232 |
+
final_metrics = {}
|
| 233 |
+
for name, values in name2values.items():
|
| 234 |
+
for stat in ["mean", "n_samples", "bootstrap_std"]:
|
| 235 |
+
key = name if stat == "mean" else f"{name}:{stat}"
|
| 236 |
+
final_metrics[key] = _compute_clipped_stats(values, stat)
|
| 237 |
+
return EvalResult(
|
| 238 |
+
score=final_metrics.pop("score", None),
|
| 239 |
+
metrics=final_metrics,
|
| 240 |
+
htmls=htmls,
|
| 241 |
+
convos=convos,
|
| 242 |
+
metadata={"example_level_metadata": metadata},
|
| 243 |
+
)
|
| 244 |
+
|
| 245 |
+
|
| 246 |
+
class HealthBenchEval(Eval):
|
| 247 |
+
def __init__(
|
| 248 |
+
self,
|
| 249 |
+
grader_model: SamplerBase,
|
| 250 |
+
num_examples: int | None = None,
|
| 251 |
+
n_repeats: int = 1,
|
| 252 |
+
# If set, evaluate human completions or reference completions instead of model completions.
|
| 253 |
+
physician_completions_mode: str | None = None,
|
| 254 |
+
# If True, run the grader on reference completions used by physicians, and physician_completions_mode must be set.
|
| 255 |
+
run_reference_completions: bool = False,
|
| 256 |
+
n_threads: int = 120,
|
| 257 |
+
subset_name: Literal["hard", "consensus"] | None = None,
|
| 258 |
+
):
|
| 259 |
+
if run_reference_completions:
|
| 260 |
+
assert physician_completions_mode is not None, (
|
| 261 |
+
"physician_completions_mode must be provided if run_reference_completions is True"
|
| 262 |
+
)
|
| 263 |
+
assert PHYSICIAN_COMPLETION_MODES[physician_completions_mode][
|
| 264 |
+
"has_reference"
|
| 265 |
+
], (
|
| 266 |
+
"physician_completions_mode must have reference completions if run_reference_completions is True"
|
| 267 |
+
)
|
| 268 |
+
|
| 269 |
+
if subset_name == "hard":
|
| 270 |
+
input_path = INPUT_PATH_HARD
|
| 271 |
+
elif subset_name == "consensus":
|
| 272 |
+
input_path = INPUT_PATH_CONSENSUS
|
| 273 |
+
elif subset_name is None:
|
| 274 |
+
input_path = INPUT_PATH
|
| 275 |
+
else:
|
| 276 |
+
assert False, f"Invalid subset name: {subset_name}"
|
| 277 |
+
with bf.BlobFile(input_path, "rb") as f:
|
| 278 |
+
examples = [json.loads(line) for line in f]
|
| 279 |
+
for example in examples:
|
| 280 |
+
example["rubrics"] = [RubricItem.from_dict(d) for d in example["rubrics"]]
|
| 281 |
+
|
| 282 |
+
rng = random.Random(0)
|
| 283 |
+
|
| 284 |
+
# physician completions mode
|
| 285 |
+
self.physician_completions_mode = physician_completions_mode
|
| 286 |
+
if self.physician_completions_mode is not None:
|
| 287 |
+
assert self.physician_completions_mode in PHYSICIAN_COMPLETION_MODES, (
|
| 288 |
+
f"Invalid physician completions mode: {self.physician_completions_mode}; must be one of {PHYSICIAN_COMPLETION_MODES.keys()}"
|
| 289 |
+
)
|
| 290 |
+
# subset to only the rows which have physician completions from that group
|
| 291 |
+
examples_matching_mode = [
|
| 292 |
+
example
|
| 293 |
+
for example in examples
|
| 294 |
+
if example["ideal_completions_data"] is not None
|
| 295 |
+
and example["ideal_completions_data"]["ideal_completions_group"]
|
| 296 |
+
== self.physician_completions_mode
|
| 297 |
+
]
|
| 298 |
+
print(
|
| 299 |
+
f"Subsetting to {len(examples_matching_mode)} examples with physician completions of type {self.physician_completions_mode} ({PHYSICIAN_COMPLETION_MODES[self.physician_completions_mode]['description']})"
|
| 300 |
+
)
|
| 301 |
+
|
| 302 |
+
examples = []
|
| 303 |
+
if run_reference_completions:
|
| 304 |
+
for example in examples_matching_mode:
|
| 305 |
+
for completion in example["ideal_completions_data"][
|
| 306 |
+
"ideal_completions_ref_completions"
|
| 307 |
+
]:
|
| 308 |
+
new_example = copy.deepcopy(example)
|
| 309 |
+
new_example["completion_to_trial"] = completion
|
| 310 |
+
examples.append(new_example)
|
| 311 |
+
assert len(examples) == len(examples_matching_mode) * 4
|
| 312 |
+
print(
|
| 313 |
+
f"Running four references for each example, for {len(examples)} total"
|
| 314 |
+
)
|
| 315 |
+
else:
|
| 316 |
+
for example in examples_matching_mode:
|
| 317 |
+
example["completion_to_trial"] = example["ideal_completions_data"][
|
| 318 |
+
"ideal_completion"
|
| 319 |
+
]
|
| 320 |
+
examples.append(example)
|
| 321 |
+
assert len(examples) == len(examples_matching_mode)
|
| 322 |
+
|
| 323 |
+
if len(examples) == 0:
|
| 324 |
+
raise ValueError(
|
| 325 |
+
f"No examples found matching mode {self.physician_completions_mode}"
|
| 326 |
+
)
|
| 327 |
+
|
| 328 |
+
if num_examples is not None and num_examples < len(examples):
|
| 329 |
+
examples = rng.sample(
|
| 330 |
+
examples,
|
| 331 |
+
num_examples,
|
| 332 |
+
)
|
| 333 |
+
|
| 334 |
+
self.examples = examples * n_repeats
|
| 335 |
+
self.n_threads = n_threads
|
| 336 |
+
self.grader_model = grader_model
|
| 337 |
+
|
| 338 |
+
def grade_sample(
|
| 339 |
+
self,
|
| 340 |
+
prompt: list[dict[str, str]],
|
| 341 |
+
response_text: str,
|
| 342 |
+
example_tags: list[str],
|
| 343 |
+
rubric_items: list[RubricItem],
|
| 344 |
+
) -> tuple[dict, str, list[dict]]:
|
| 345 |
+
# construct and grade the sample
|
| 346 |
+
convo_with_response = prompt + [dict(content=response_text, role="assistant")]
|
| 347 |
+
|
| 348 |
+
def grade_rubric_item(rubric_item: RubricItem) -> dict:
|
| 349 |
+
convo_str = "\n\n".join(
|
| 350 |
+
[f"{m['role']}: {m['content']}" for m in convo_with_response]
|
| 351 |
+
)
|
| 352 |
+
grader_prompt = GRADER_TEMPLATE.replace(
|
| 353 |
+
"<<conversation>>", convo_str
|
| 354 |
+
).replace("<<rubric_item>>", str(rubric_item))
|
| 355 |
+
messages: MessageList = [dict(content=grader_prompt, role="user")]
|
| 356 |
+
while True:
|
| 357 |
+
sampler_response = self.grader_model(messages)
|
| 358 |
+
grading_response = sampler_response.response_text
|
| 359 |
+
grading_response_dict = parse_json_to_dict(grading_response)
|
| 360 |
+
if "criteria_met" in grading_response_dict:
|
| 361 |
+
label = grading_response_dict["criteria_met"]
|
| 362 |
+
if label is True or label is False:
|
| 363 |
+
break
|
| 364 |
+
print("Grading failed due to bad JSON output, retrying...")
|
| 365 |
+
return grading_response_dict
|
| 366 |
+
|
| 367 |
+
grading_response_list = report.map_with_progress(
|
| 368 |
+
grade_rubric_item,
|
| 369 |
+
rubric_items,
|
| 370 |
+
pbar=False,
|
| 371 |
+
)
|
| 372 |
+
|
| 373 |
+
# compute the overall score
|
| 374 |
+
overall_score = calculate_score(rubric_items, grading_response_list)
|
| 375 |
+
assert overall_score is not None
|
| 376 |
+
metrics = {
|
| 377 |
+
"overall_score": overall_score,
|
| 378 |
+
}
|
| 379 |
+
|
| 380 |
+
# compute scores for example-level tags)
|
| 381 |
+
example_tag_scores = {tag: overall_score for tag in example_tags}
|
| 382 |
+
assert len(example_tag_scores) == len(example_tags) # No duplicates.
|
| 383 |
+
metrics.update(example_tag_scores)
|
| 384 |
+
|
| 385 |
+
# compute scores for rubric-level tags
|
| 386 |
+
rubric_tag_items_grades = defaultdict(list)
|
| 387 |
+
for rubric_item, grading_response in zip(rubric_items, grading_response_list):
|
| 388 |
+
curr_item_tags = set() # Ensure no duplicates in a rubric item.
|
| 389 |
+
for tag in rubric_item.tags:
|
| 390 |
+
rubric_tag_items_grades[tag].append((rubric_item, grading_response))
|
| 391 |
+
assert tag not in curr_item_tags
|
| 392 |
+
curr_item_tags.add(tag)
|
| 393 |
+
|
| 394 |
+
rubric_tag_scores = {}
|
| 395 |
+
for tag, items_grades in rubric_tag_items_grades.items():
|
| 396 |
+
items, grades = zip(*items_grades)
|
| 397 |
+
score = calculate_score(items, grades)
|
| 398 |
+
if score is not None: # implies at least one positive criterion
|
| 399 |
+
rubric_tag_scores[tag] = score
|
| 400 |
+
metrics.update(rubric_tag_scores)
|
| 401 |
+
|
| 402 |
+
# construct the list of explanations and grades
|
| 403 |
+
rubric_items_with_grades = []
|
| 404 |
+
readable_explanation_list = []
|
| 405 |
+
for rubric_item, grading_response in zip(rubric_items, grading_response_list):
|
| 406 |
+
explanation = grading_response.get("explanation", "No explanation provided")
|
| 407 |
+
criteria_met = grading_response["criteria_met"]
|
| 408 |
+
readable_explanation = (
|
| 409 |
+
f"[{criteria_met}] {rubric_item}\n\tExplanation: {explanation}"
|
| 410 |
+
)
|
| 411 |
+
readable_explanation_list.append(readable_explanation)
|
| 412 |
+
rubric_items_with_grades.append(
|
| 413 |
+
{
|
| 414 |
+
**rubric_item.to_dict(),
|
| 415 |
+
"criteria_met": criteria_met,
|
| 416 |
+
"explanation": explanation,
|
| 417 |
+
}
|
| 418 |
+
)
|
| 419 |
+
|
| 420 |
+
readable_explanation_list.sort(
|
| 421 |
+
key=lambda x: x.startswith("[False]"), reverse=True
|
| 422 |
+
)
|
| 423 |
+
readable_explanation_str = "\n\n".join(readable_explanation_list)
|
| 424 |
+
readable_explanation_str = f"\n\n{readable_explanation_str}"
|
| 425 |
+
|
| 426 |
+
return metrics, readable_explanation_str, rubric_items_with_grades
|
| 427 |
+
|
| 428 |
+
def __call__(self, sampler: SamplerBase) -> EvalResult:
|
| 429 |
+
def fn(row: dict):
|
| 430 |
+
prompt_messages = row["prompt"]
|
| 431 |
+
|
| 432 |
+
if self.physician_completions_mode is not None:
|
| 433 |
+
response_text = row["completion_to_trial"]
|
| 434 |
+
response_usage = None
|
| 435 |
+
actual_queried_prompt_messages = prompt_messages
|
| 436 |
+
else:
|
| 437 |
+
sampler_response = sampler(prompt_messages)
|
| 438 |
+
response_text = sampler_response.response_text
|
| 439 |
+
response_dict = sampler_response.response_metadata
|
| 440 |
+
actual_queried_prompt_messages = (
|
| 441 |
+
sampler_response.actual_queried_message_list
|
| 442 |
+
)
|
| 443 |
+
response_usage = response_dict.get("usage", None)
|
| 444 |
+
|
| 445 |
+
metrics, readable_explanation_str, rubric_items_with_grades = (
|
| 446 |
+
self.grade_sample(
|
| 447 |
+
prompt=actual_queried_prompt_messages,
|
| 448 |
+
response_text=response_text,
|
| 449 |
+
rubric_items=row["rubrics"],
|
| 450 |
+
example_tags=row["example_tags"],
|
| 451 |
+
)
|
| 452 |
+
)
|
| 453 |
+
|
| 454 |
+
score = metrics["overall_score"]
|
| 455 |
+
|
| 456 |
+
# Create HTML for each sample result
|
| 457 |
+
html = report.jinja_env.from_string(
|
| 458 |
+
HEALTHBENCH_HTML_JINJA.replace(
|
| 459 |
+
"{{ rubric_grades }}",
|
| 460 |
+
readable_explanation_str.replace("\n", "<br>"),
|
| 461 |
+
)
|
| 462 |
+
).render(
|
| 463 |
+
prompt_messages=actual_queried_prompt_messages,
|
| 464 |
+
next_message=dict(content=response_text, role="assistant"),
|
| 465 |
+
score=metrics["overall_score"],
|
| 466 |
+
extracted_answer=response_text,
|
| 467 |
+
)
|
| 468 |
+
|
| 469 |
+
convo = actual_queried_prompt_messages + [
|
| 470 |
+
dict(content=response_text, role="assistant")
|
| 471 |
+
]
|
| 472 |
+
return SingleEvalResult(
|
| 473 |
+
html=html,
|
| 474 |
+
score=score,
|
| 475 |
+
convo=convo,
|
| 476 |
+
metrics=metrics,
|
| 477 |
+
example_level_metadata={
|
| 478 |
+
"score": score,
|
| 479 |
+
"usage": get_usage_dict(response_usage),
|
| 480 |
+
"rubric_items": rubric_items_with_grades,
|
| 481 |
+
"prompt": actual_queried_prompt_messages,
|
| 482 |
+
"completion": [dict(content=response_text, role="assistant")],
|
| 483 |
+
"prompt_id": row["prompt_id"],
|
| 484 |
+
"completion_id": hashlib.sha256(
|
| 485 |
+
(row["prompt_id"] + response_text).encode("utf-8")
|
| 486 |
+
).hexdigest(),
|
| 487 |
+
},
|
| 488 |
+
)
|
| 489 |
+
|
| 490 |
+
results = report.map_with_progress(
|
| 491 |
+
fn,
|
| 492 |
+
self.examples,
|
| 493 |
+
num_threads=self.n_threads,
|
| 494 |
+
pbar=True,
|
| 495 |
+
)
|
| 496 |
+
final_metrics = _aggregate_get_clipped_mean(results)
|
| 497 |
+
return final_metrics
|
| 498 |
+
|
| 499 |
+
|
| 500 |
+
def main():
|
| 501 |
+
parser = argparse.ArgumentParser(
|
| 502 |
+
description="HealthBenchEval specific run options, including e.g., running the eval on physician completions rows only."
|
| 503 |
+
)
|
| 504 |
+
parser.add_argument(
|
| 505 |
+
"--run_mode",
|
| 506 |
+
type=str,
|
| 507 |
+
choices=["physician_completions", "physician_completion_references"],
|
| 508 |
+
)
|
| 509 |
+
parser.add_argument("--examples", type=int, help="Number of examples to run")
|
| 510 |
+
parser.add_argument(
|
| 511 |
+
"--n-threads",
|
| 512 |
+
type=int,
|
| 513 |
+
default=120,
|
| 514 |
+
help="Number of threads to run",
|
| 515 |
+
)
|
| 516 |
+
args = parser.parse_args()
|
| 517 |
+
|
| 518 |
+
if args.run_mode == "physician_completions":
|
| 519 |
+
physician_completions_main(
|
| 520 |
+
run_reference_completions=False,
|
| 521 |
+
num_examples=args.examples,
|
| 522 |
+
n_threads=args.n_threads or 1,
|
| 523 |
+
)
|
| 524 |
+
elif args.run_mode == "physician_completion_references":
|
| 525 |
+
physician_completions_main(
|
| 526 |
+
run_reference_completions=True,
|
| 527 |
+
num_examples=args.examples,
|
| 528 |
+
n_threads=args.n_threads or 1,
|
| 529 |
+
)
|
| 530 |
+
|
| 531 |
+
else:
|
| 532 |
+
raise ValueError(f"Invalid run mode: {args.run_mode}")
|
| 533 |
+
|
| 534 |
+
|
| 535 |
+
def physician_completions_main(
|
| 536 |
+
run_reference_completions: bool = False,
|
| 537 |
+
num_examples: int | None = None,
|
| 538 |
+
n_threads: int = 120,
|
| 539 |
+
):
|
| 540 |
+
now = datetime.now()
|
| 541 |
+
date_str = now.strftime("%Y%m%d_%H%M")
|
| 542 |
+
|
| 543 |
+
grading_sampler = ChatCompletionsSampler(
|
| 544 |
+
model="gpt-4.1-2025-04-14",
|
| 545 |
+
system_message=OPENAI_SYSTEM_MESSAGE_API,
|
| 546 |
+
max_tokens=2048,
|
| 547 |
+
base_url="https://api.openai.com/v1",
|
| 548 |
+
)
|
| 549 |
+
dummy_sampler = SamplerBase()
|
| 550 |
+
|
| 551 |
+
merge_metrics = []
|
| 552 |
+
for pc_mode in PHYSICIAN_COMPLETION_MODES.keys():
|
| 553 |
+
if (
|
| 554 |
+
run_reference_completions
|
| 555 |
+
and not PHYSICIAN_COMPLETION_MODES[pc_mode]["has_reference"]
|
| 556 |
+
):
|
| 557 |
+
continue
|
| 558 |
+
|
| 559 |
+
# run
|
| 560 |
+
eval = HealthBenchEval(
|
| 561 |
+
grader_model=grading_sampler,
|
| 562 |
+
physician_completions_mode=pc_mode,
|
| 563 |
+
run_reference_completions=run_reference_completions,
|
| 564 |
+
num_examples=num_examples,
|
| 565 |
+
n_threads=n_threads,
|
| 566 |
+
)
|
| 567 |
+
result = eval(dummy_sampler)
|
| 568 |
+
|
| 569 |
+
# report
|
| 570 |
+
parsable_mode = PHYSICIAN_COMPLETION_MODES[pc_mode]["short_name"]
|
| 571 |
+
if run_reference_completions:
|
| 572 |
+
file_stem = f"healthbench_{parsable_mode}_referencecompletions_{date_str}"
|
| 573 |
+
else:
|
| 574 |
+
file_stem = f"healthbench_{parsable_mode}_humanbaseline_{date_str}"
|
| 575 |
+
report_filename = Path(f"/tmp/{file_stem}.html")
|
| 576 |
+
report_filename.write_text(report.make_report(result))
|
| 577 |
+
print(f"Report saved to {report_filename}")
|
| 578 |
+
|
| 579 |
+
# metrics
|
| 580 |
+
assert result.metrics is not None
|
| 581 |
+
metrics = result.metrics
|
| 582 |
+
result_filename = Path(f"/tmp/{file_stem}.json")
|
| 583 |
+
result_filename.write_text(json.dumps(metrics))
|
| 584 |
+
print(f"Results saved to {result_filename}")
|
| 585 |
+
|
| 586 |
+
full_result_dict = {
|
| 587 |
+
"score": result.score,
|
| 588 |
+
"metrics": result.metrics,
|
| 589 |
+
"htmls": result.htmls,
|
| 590 |
+
"convos": result.convos,
|
| 591 |
+
"metadata": result.metadata,
|
| 592 |
+
}
|
| 593 |
+
full_result_filename = Path(f"/tmp/{file_stem}_allresults.json")
|
| 594 |
+
full_result_filename.write_text(json.dumps(full_result_dict, indent=2))
|
| 595 |
+
print(f"All results saved to {full_result_filename}")
|
| 596 |
+
|
| 597 |
+
# metrics df
|
| 598 |
+
merge_metrics.append(
|
| 599 |
+
{
|
| 600 |
+
"eval_name": "healthbench",
|
| 601 |
+
"model_name": f"{pc_mode} ({PHYSICIAN_COMPLETION_MODES[pc_mode]['description']})",
|
| 602 |
+
"metric": metrics.get("overall_score", None),
|
| 603 |
+
}
|
| 604 |
+
)
|
| 605 |
+
|
| 606 |
+
print("\nAll results: ")
|
| 607 |
+
print(merge_metrics)
|
| 608 |
+
return merge_metrics
|
| 609 |
+
|
| 610 |
+
|
| 611 |
+
if __name__ == "__main__":
|
| 612 |
+
main()
|
gpt_oss/evals/report.py
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from collections import defaultdict
|
| 3 |
+
from multiprocessing.pool import ThreadPool
|
| 4 |
+
from typing import Any, Callable
|
| 5 |
+
|
| 6 |
+
import jinja2
|
| 7 |
+
import numpy as np
|
| 8 |
+
from tqdm import tqdm
|
| 9 |
+
|
| 10 |
+
from .types import EvalResult, Message, SingleEvalResult
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
HTML_JINJA = """
|
| 14 |
+
<h3>Prompt conversation</h3>
|
| 15 |
+
{% for message in prompt_messages %}
|
| 16 |
+
{{ message_to_html(message) | safe }}
|
| 17 |
+
{% endfor %}
|
| 18 |
+
<h3>Sampled message</h3>
|
| 19 |
+
{{ message_to_html(next_message) | safe }}
|
| 20 |
+
<h3>Results</h3>
|
| 21 |
+
<p>Correct Answer: {{ correct_answer }}</p>
|
| 22 |
+
<p>Extracted Answer: {{ extracted_answer }}</p>
|
| 23 |
+
<p>Score: {{ score }}</p>
|
| 24 |
+
"""
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def _compute_stat(values: list, stat: str):
|
| 28 |
+
if stat == "mean":
|
| 29 |
+
return np.mean(values)
|
| 30 |
+
elif stat == "std":
|
| 31 |
+
return np.std(values)
|
| 32 |
+
elif stat == "min":
|
| 33 |
+
return np.min(values)
|
| 34 |
+
elif stat == "max":
|
| 35 |
+
return np.max(values)
|
| 36 |
+
elif stat == "n_samples":
|
| 37 |
+
return len(values)
|
| 38 |
+
elif stat == "bootstrap_std":
|
| 39 |
+
return np.std(
|
| 40 |
+
[np.mean(np.random.choice(values, len(values))) for _ in range(1000)]
|
| 41 |
+
)
|
| 42 |
+
else:
|
| 43 |
+
raise ValueError(f"Unknown {stat =}")
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def aggregate_results(
|
| 47 |
+
single_eval_results: list[SingleEvalResult],
|
| 48 |
+
default_stats: tuple[str, ...] = ("mean", "std"),
|
| 49 |
+
name2stats: dict[str, tuple[str]] | None = None,
|
| 50 |
+
) -> EvalResult:
|
| 51 |
+
"""
|
| 52 |
+
Aggregate results from multiple evaluations into a single EvalResult.
|
| 53 |
+
"""
|
| 54 |
+
name2stats = name2stats or {}
|
| 55 |
+
name2values = defaultdict(list)
|
| 56 |
+
htmls = []
|
| 57 |
+
convos = []
|
| 58 |
+
metadata = []
|
| 59 |
+
for single_eval_result in single_eval_results:
|
| 60 |
+
for name, value in single_eval_result.metrics.items():
|
| 61 |
+
name2values[name].append(value)
|
| 62 |
+
if single_eval_result.score is not None:
|
| 63 |
+
name2values["score"].append(single_eval_result.score)
|
| 64 |
+
htmls.append(single_eval_result.html)
|
| 65 |
+
convos.append(single_eval_result.convo)
|
| 66 |
+
metadata.append(single_eval_result.example_level_metadata)
|
| 67 |
+
final_metrics = {}
|
| 68 |
+
for name, values in name2values.items():
|
| 69 |
+
stats = name2stats.get(name, default_stats)
|
| 70 |
+
for stat in stats:
|
| 71 |
+
key = name if stat == "mean" else f"{name}:{stat}"
|
| 72 |
+
final_metrics[key] = _compute_stat(values, stat)
|
| 73 |
+
return EvalResult(
|
| 74 |
+
score=final_metrics.pop("score", None),
|
| 75 |
+
metrics=final_metrics,
|
| 76 |
+
htmls=htmls,
|
| 77 |
+
convos=convos,
|
| 78 |
+
metadata={"example_level_metadata": metadata},
|
| 79 |
+
)
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
def map_with_progress(
|
| 83 |
+
f: Callable,
|
| 84 |
+
xs: list[Any],
|
| 85 |
+
num_threads: int = 128,
|
| 86 |
+
pbar: bool = True,
|
| 87 |
+
):
|
| 88 |
+
"""
|
| 89 |
+
Apply f to each element of xs, using a ThreadPool, and show progress.
|
| 90 |
+
"""
|
| 91 |
+
pbar_fn = tqdm if pbar else lambda x, *args, **kwargs: x
|
| 92 |
+
|
| 93 |
+
if os.getenv("debug"):
|
| 94 |
+
return list(map(f, pbar_fn(xs, total=len(xs))))
|
| 95 |
+
else:
|
| 96 |
+
with ThreadPool(min(num_threads, len(xs))) as pool:
|
| 97 |
+
return list(pbar_fn(pool.imap_unordered(f, xs), total=len(xs)))
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
jinja_env = jinja2.Environment(
|
| 101 |
+
loader=jinja2.BaseLoader(),
|
| 102 |
+
undefined=jinja2.StrictUndefined,
|
| 103 |
+
autoescape=jinja2.select_autoescape(["html", "xml"]),
|
| 104 |
+
)
|
| 105 |
+
_message_template = """
|
| 106 |
+
<div class="message {{ role }}">
|
| 107 |
+
<div class="role">
|
| 108 |
+
{{ role }}
|
| 109 |
+
{% if variant %}<span class="variant">({{ variant }})</span>{% endif %}
|
| 110 |
+
</div>
|
| 111 |
+
<div class="content">
|
| 112 |
+
<pre>{{ content }}</pre>
|
| 113 |
+
</div>
|
| 114 |
+
</div>
|
| 115 |
+
"""
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
def message_to_html(message: Message) -> str:
|
| 119 |
+
"""
|
| 120 |
+
Generate HTML snippet (inside a <div>) for a message.
|
| 121 |
+
"""
|
| 122 |
+
return jinja_env.from_string(_message_template).render(
|
| 123 |
+
role=message["role"],
|
| 124 |
+
content=message["content"],
|
| 125 |
+
variant=message.get("variant", None),
|
| 126 |
+
)
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
jinja_env.globals["message_to_html"] = message_to_html
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
_report_template = """<!DOCTYPE html>
|
| 133 |
+
<html>
|
| 134 |
+
<head>
|
| 135 |
+
<meta charset="utf-8">
|
| 136 |
+
<style>
|
| 137 |
+
.message {
|
| 138 |
+
padding: 8px 16px;
|
| 139 |
+
margin-bottom: 8px;
|
| 140 |
+
border-radius: 4px;
|
| 141 |
+
}
|
| 142 |
+
.message.user {
|
| 143 |
+
background-color: #B2DFDB;
|
| 144 |
+
color: #00695C;
|
| 145 |
+
}
|
| 146 |
+
.message.assistant {
|
| 147 |
+
background-color: #B39DDB;
|
| 148 |
+
color: #4527A0;
|
| 149 |
+
}
|
| 150 |
+
.message.system {
|
| 151 |
+
background-color: #EEEEEE;
|
| 152 |
+
color: #212121;
|
| 153 |
+
}
|
| 154 |
+
.role {
|
| 155 |
+
font-weight: bold;
|
| 156 |
+
margin-bottom: 4px;
|
| 157 |
+
}
|
| 158 |
+
.variant {
|
| 159 |
+
color: #795548;
|
| 160 |
+
}
|
| 161 |
+
table, th, td {
|
| 162 |
+
border: 1px solid black;
|
| 163 |
+
}
|
| 164 |
+
pre {
|
| 165 |
+
white-space: pre-wrap;
|
| 166 |
+
}
|
| 167 |
+
</style>
|
| 168 |
+
</head>
|
| 169 |
+
<body>
|
| 170 |
+
{% if metrics %}
|
| 171 |
+
<h1>Metrics</h1>
|
| 172 |
+
<table>
|
| 173 |
+
<tr>
|
| 174 |
+
<th>Metric</th>
|
| 175 |
+
<th>Value</th>
|
| 176 |
+
</tr>
|
| 177 |
+
<tr>
|
| 178 |
+
<td><b>Score</b></td>
|
| 179 |
+
<td>{{ score | float | round(3) }}</td>
|
| 180 |
+
</tr>
|
| 181 |
+
{% for name, value in metrics.items() %}
|
| 182 |
+
<tr>
|
| 183 |
+
<td>{{ name }}</td>
|
| 184 |
+
<td>{{ value }}</td>
|
| 185 |
+
</tr>
|
| 186 |
+
{% endfor %}
|
| 187 |
+
</table>
|
| 188 |
+
{% endif %}
|
| 189 |
+
<h1>Examples</h1>
|
| 190 |
+
{% for html in htmls %}
|
| 191 |
+
{{ html | safe }}
|
| 192 |
+
<hr>
|
| 193 |
+
{% endfor %}
|
| 194 |
+
</body>
|
| 195 |
+
</html>
|
| 196 |
+
"""
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
def make_report(eval_result: EvalResult) -> str:
|
| 200 |
+
"""
|
| 201 |
+
Create a standalone HTML report from an EvalResult.
|
| 202 |
+
"""
|
| 203 |
+
return jinja_env.from_string(_report_template).render(
|
| 204 |
+
score=eval_result.score,
|
| 205 |
+
metrics=eval_result.metrics,
|
| 206 |
+
htmls=eval_result.htmls,
|
| 207 |
+
)
|
gpt_oss/evals/responses_sampler.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import time
|
| 2 |
+
from typing import Any
|
| 3 |
+
|
| 4 |
+
import openai
|
| 5 |
+
from openai import OpenAI
|
| 6 |
+
|
| 7 |
+
from .types import MessageList, SamplerBase, SamplerResponse
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
class ResponsesSampler(SamplerBase):
|
| 11 |
+
"""
|
| 12 |
+
Sample from OpenAI's responses API
|
| 13 |
+
"""
|
| 14 |
+
|
| 15 |
+
def __init__(
|
| 16 |
+
self,
|
| 17 |
+
model: str,
|
| 18 |
+
developer_message: str | None = None,
|
| 19 |
+
temperature: float = 1.0,
|
| 20 |
+
max_tokens: int = 131_072,
|
| 21 |
+
reasoning_model: bool = False,
|
| 22 |
+
reasoning_effort: str | None = None,
|
| 23 |
+
base_url: str = "http://localhost:8000/v1",
|
| 24 |
+
):
|
| 25 |
+
self.client = OpenAI(base_url=base_url, timeout=24*60*60)
|
| 26 |
+
self.model = model
|
| 27 |
+
self.developer_message = developer_message
|
| 28 |
+
self.temperature = temperature
|
| 29 |
+
self.max_tokens = max_tokens
|
| 30 |
+
self.image_format = "url"
|
| 31 |
+
self.reasoning_model = reasoning_model
|
| 32 |
+
self.reasoning_effort = reasoning_effort
|
| 33 |
+
|
| 34 |
+
def _pack_message(self, role: str, content: Any) -> dict[str, Any]:
|
| 35 |
+
return {"role": role, "content": content}
|
| 36 |
+
|
| 37 |
+
def __call__(self, message_list: MessageList) -> SamplerResponse:
|
| 38 |
+
if self.developer_message:
|
| 39 |
+
message_list = [
|
| 40 |
+
self._pack_message("developer", self.developer_message)
|
| 41 |
+
] + message_list
|
| 42 |
+
trial = 0
|
| 43 |
+
while True:
|
| 44 |
+
try:
|
| 45 |
+
request_kwargs = {
|
| 46 |
+
"model": self.model,
|
| 47 |
+
"input": message_list,
|
| 48 |
+
"temperature": self.temperature,
|
| 49 |
+
"max_output_tokens": self.max_tokens,
|
| 50 |
+
}
|
| 51 |
+
if self.reasoning_model:
|
| 52 |
+
request_kwargs["reasoning"] = (
|
| 53 |
+
{"effort": self.reasoning_effort} if self.reasoning_effort else None
|
| 54 |
+
)
|
| 55 |
+
response = self.client.responses.create(**request_kwargs)
|
| 56 |
+
|
| 57 |
+
for output in response.output:
|
| 58 |
+
if hasattr(output, "text"):
|
| 59 |
+
message_list.append(self._pack_message(getattr(output, "role", "assistant"), output.text))
|
| 60 |
+
elif hasattr(output, "content"):
|
| 61 |
+
for c in output.content:
|
| 62 |
+
# c.text handled below
|
| 63 |
+
pass
|
| 64 |
+
|
| 65 |
+
return SamplerResponse(
|
| 66 |
+
response_text=response.output_text,
|
| 67 |
+
response_metadata={"usage": response.usage},
|
| 68 |
+
actual_queried_message_list=message_list,
|
| 69 |
+
)
|
| 70 |
+
except openai.BadRequestError as e:
|
| 71 |
+
print("Bad Request Error", e)
|
| 72 |
+
return SamplerResponse(
|
| 73 |
+
response_text="",
|
| 74 |
+
response_metadata={"usage": None},
|
| 75 |
+
actual_queried_message_list=message_list,
|
| 76 |
+
)
|
| 77 |
+
except Exception as e:
|
| 78 |
+
exception_backoff = 2**trial # expontial back off
|
| 79 |
+
print(
|
| 80 |
+
f"Rate limit exception so wait and retry {trial} after {exception_backoff} sec",
|
| 81 |
+
e,
|
| 82 |
+
)
|
| 83 |
+
time.sleep(exception_backoff)
|
| 84 |
+
trial += 1
|
| 85 |
+
# unknown error shall throw exception
|
gpt_oss/evals/types.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from dataclasses import dataclass, field
|
| 2 |
+
from typing import Any, Literal, overload
|
| 3 |
+
|
| 4 |
+
Message = dict[str, Any] # keys role, content
|
| 5 |
+
MessageList = list[Message]
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
@dataclass
|
| 10 |
+
class SamplerResponse:
|
| 11 |
+
"""
|
| 12 |
+
Response from a sampler.
|
| 13 |
+
"""
|
| 14 |
+
response_text: str
|
| 15 |
+
actual_queried_message_list: MessageList
|
| 16 |
+
response_metadata: dict[str, Any]
|
| 17 |
+
|
| 18 |
+
class SamplerBase:
|
| 19 |
+
"""
|
| 20 |
+
Base class for defining a sampling model, which can be evaluated,
|
| 21 |
+
or used as part of the grading process.
|
| 22 |
+
"""
|
| 23 |
+
|
| 24 |
+
def __call__(
|
| 25 |
+
self,
|
| 26 |
+
message_list: MessageList,
|
| 27 |
+
) -> SamplerResponse:
|
| 28 |
+
raise NotImplementedError
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
@dataclass
|
| 32 |
+
class EvalResult:
|
| 33 |
+
"""
|
| 34 |
+
Result of running an evaluation (usually consisting of many samples)
|
| 35 |
+
"""
|
| 36 |
+
|
| 37 |
+
score: float | None # top-line metric
|
| 38 |
+
metrics: dict[str, float] | None # other metrics
|
| 39 |
+
htmls: list[str] # strings of valid HTML
|
| 40 |
+
convos: list[MessageList] # sampled conversations
|
| 41 |
+
metadata: dict[str, Any] | None # Extra data such as rubric scores or sollen
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
@dataclass
|
| 45 |
+
class SingleEvalResult:
|
| 46 |
+
"""
|
| 47 |
+
Result of evaluating a single sample
|
| 48 |
+
"""
|
| 49 |
+
|
| 50 |
+
score: float | None
|
| 51 |
+
metrics: dict[str, float] = field(default_factory=dict)
|
| 52 |
+
html: str | None = None
|
| 53 |
+
convo: MessageList | None = None # sampled conversation
|
| 54 |
+
example_level_metadata: dict[str, Any] | None = (
|
| 55 |
+
None # Extra data such as rubric scores or sollen
|
| 56 |
+
)
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
class Eval:
|
| 60 |
+
"""
|
| 61 |
+
Base class for defining an evaluation.
|
| 62 |
+
"""
|
| 63 |
+
|
| 64 |
+
def __call__(self, sampler: SamplerBase) -> EvalResult:
|
| 65 |
+
raise NotImplementedError
|
| 66 |
+
|
gpt_oss/generate.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Model parallel inference
|
| 2 |
+
# Note: This script is for demonstration purposes only. It is not designed for production use.
|
| 3 |
+
# See gpt_oss.chat for a more complete example with the Harmony parser.
|
| 4 |
+
# torchrun --nproc-per-node=4 -m gpt_oss.generate -p "why did the chicken cross the road?" model/
|
| 5 |
+
|
| 6 |
+
import argparse
|
| 7 |
+
|
| 8 |
+
from gpt_oss.tokenizer import get_tokenizer
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def main(args):
|
| 12 |
+
match args.backend:
|
| 13 |
+
case "torch":
|
| 14 |
+
from gpt_oss.torch.utils import init_distributed
|
| 15 |
+
from gpt_oss.torch.model import TokenGenerator as TorchGenerator
|
| 16 |
+
device = init_distributed()
|
| 17 |
+
generator = TorchGenerator(args.checkpoint, device=device)
|
| 18 |
+
case "triton":
|
| 19 |
+
from gpt_oss.torch.utils import init_distributed
|
| 20 |
+
from gpt_oss.triton.model import TokenGenerator as TritonGenerator
|
| 21 |
+
device = init_distributed()
|
| 22 |
+
generator = TritonGenerator(args.checkpoint, context=args.context_length, device=device)
|
| 23 |
+
case "vllm":
|
| 24 |
+
from gpt_oss.vllm.token_generator import TokenGenerator as VLLMGenerator
|
| 25 |
+
generator = VLLMGenerator(args.checkpoint, tensor_parallel_size=args.tensor_parallel_size)
|
| 26 |
+
case _:
|
| 27 |
+
raise ValueError(f"Invalid backend: {args.backend}")
|
| 28 |
+
|
| 29 |
+
tokenizer = get_tokenizer()
|
| 30 |
+
tokens = tokenizer.encode(args.prompt)
|
| 31 |
+
max_tokens = None if args.limit == 0 else args.limit
|
| 32 |
+
for token, logprob in generator.generate(tokens, stop_tokens=[tokenizer.eot_token], temperature=args.temperature, max_tokens=max_tokens, return_logprobs=True):
|
| 33 |
+
tokens.append(token)
|
| 34 |
+
token_text = tokenizer.decode([token])
|
| 35 |
+
print(
|
| 36 |
+
f"Generated token: {repr(token_text)}, logprob: {logprob}"
|
| 37 |
+
)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
if __name__ == "__main__":
|
| 41 |
+
parser = argparse.ArgumentParser(description="Text generation example")
|
| 42 |
+
parser.add_argument(
|
| 43 |
+
"checkpoint",
|
| 44 |
+
metavar="FILE",
|
| 45 |
+
type=str,
|
| 46 |
+
help="Path to the SafeTensors checkpoint",
|
| 47 |
+
)
|
| 48 |
+
parser.add_argument(
|
| 49 |
+
"-p",
|
| 50 |
+
"--prompt",
|
| 51 |
+
metavar="PROMPT",
|
| 52 |
+
type=str,
|
| 53 |
+
default="How are you?",
|
| 54 |
+
help="LLM prompt",
|
| 55 |
+
)
|
| 56 |
+
parser.add_argument(
|
| 57 |
+
"-t",
|
| 58 |
+
"--temperature",
|
| 59 |
+
metavar="TEMP",
|
| 60 |
+
type=float,
|
| 61 |
+
default=0.0,
|
| 62 |
+
help="Sampling temperature",
|
| 63 |
+
)
|
| 64 |
+
parser.add_argument(
|
| 65 |
+
"-l",
|
| 66 |
+
"--limit",
|
| 67 |
+
metavar="LIMIT",
|
| 68 |
+
type=int,
|
| 69 |
+
default=0,
|
| 70 |
+
help="Limit on the number of tokens (0 to disable)",
|
| 71 |
+
)
|
| 72 |
+
parser.add_argument(
|
| 73 |
+
"-b",
|
| 74 |
+
"--backend",
|
| 75 |
+
metavar="BACKEND",
|
| 76 |
+
type=str,
|
| 77 |
+
default="torch",
|
| 78 |
+
choices=["triton", "torch", "vllm"],
|
| 79 |
+
help="Inference backend",
|
| 80 |
+
)
|
| 81 |
+
parser.add_argument(
|
| 82 |
+
"--tensor-parallel-size",
|
| 83 |
+
type=int,
|
| 84 |
+
default=2,
|
| 85 |
+
help="Tensor parallel size for vLLM backend",
|
| 86 |
+
)
|
| 87 |
+
parser.add_argument(
|
| 88 |
+
"--context-length",
|
| 89 |
+
type=int,
|
| 90 |
+
default=4096,
|
| 91 |
+
help="Context length for Triton backend",
|
| 92 |
+
)
|
| 93 |
+
args = parser.parse_args()
|
| 94 |
+
|
| 95 |
+
main(args)
|