
Do not treat the reply as the step the run took, and do not ask the model which step it was, until you can name the step. Tonight, three proofs, from a log you stored. Name the step that run actually took: the step id, the type, and the status. Show a different step whose id, or whose type, is not this one. Show that another run's list does not hold this step. If the log does not keep the step, write that. A fluent answer is not an id.
This is one step on one run. It is not the run. Which run did that thread start? is the run id, the status, and the assistant. A run can complete while this step id was never stored, and a step can complete while the run id is a different string. It is not the tool result. Which tool result did the model see? is the result the model was shown. A tool call inside a step is not the whole step. It is not the method a customer stored. Which method did that customer store? is a payment method id. It is not the issue a repository opened. Which issue did that repository open? is an issue number. A step id is none of those.
None of what follows is a score for this product, and none of it is a promise that a benchmark's judge saw your log. The fields below are one vendor's current samples of a run step. The list example and the streaming examples do not share an id. The counts below that are a paper about other people's API calls. A count of APIs is not the step you stored tonight.
OpenAI's list example returns one step and then names a last id that is not in the list. The object is list. data holds one step. The id is step_abc123. 11 characters. object is thread.run.step. 15 characters. 15 is not 11. created_at is 1699063291. completed_at is 1699063291. The gap is 0 seconds. run_id is run_abc123. 10 characters. assistant_id is asst_abc123. 11 characters, the same length as the step id, and not the same string. thread_id is thread_abc123. 13 characters. type is message_creation. 16 characters. status is completed. 9 characters. cancelled_at, expired_at, failed_at, and last_error are null. The field name is expired_at. 10 characters. step_details.type is message_creation, and message_id is msg_abc123. 10 characters, the same length as the run id, and not the same string. usage prints prompt_tokens 123, completion_tokens 456, and total_tokens 579. 123 plus 456 is 579. That addition matches. It does not make 579 a step id. first_id is step_abc123. last_id is step_abc456. Also 11 characters, and not the id in data. has_more is false. One object, and the first id is not the last id. Do not invent a second step to make last_id true, and do not delete last_id because has_more is false. The retrieve example prints the same step id, the same created time, the same 579, and no first_id and no last_id. A retrieve is not a list. Do not paste step_abc456 onto the retrieve because the list's first_id matches (OpenAI, API definition).
The limits and the status names are not the sample's length. limit can range from 1 to 100, and the default is 20. 20 is not 100. The list example does not send a limit, and data holds 1 step. 1 is not 20. The pagination sentence talks about receiving 100 objects. 100 is the maximum. It is not the sample. order defaults to desc. The other value is asc. Two names. The status can be in_progress, cancelled, failed, completed, or expired. Five names. The description says either, and then lists five. 5 is not 2. completed is the list sample. in_progress is not completed. expired as a status is not the field expired_at, and a null expired_at is not status expired. The type can be message_creation or tool_calls. Two names. 16 is not 10. A last_error code, when it is set, is server_error or rate_limit_exceeded. Two names. The sample's last_error is null. The schema lists metadata as required. The list example and the retrieve example do not print metadata. Do not add an empty metadata object because the schema wants one. The examples are what they print.
The streaming examples reuse one id across three created times, and they use a different expiry field. The id is step_001. 8 characters. 8 is not 11, so step_001 is not step_abc123. One message-creation event has created_at 1710348076, status in_progress, completed_at null, and expires_at 1710348675. The field name is expires_at. 10 characters, the same length as expired_at, and not the same string. 1710348675 minus 1710348076 is 599 seconds, which is 9 minutes and 59 seconds. 599 is not 600. The completed event on that same created time sets completed_at to 1710348077, one second later, and usage to prompt_tokens 20, completion_tokens 11, and total_tokens 31. 20 plus 11 is 31. That addition matches. 31 is not 579. 20 is not 123. 11 is not 456. A tool-calls event uses the same id step_001, created_at 1710351819, status in_progress, an empty tool_calls array, usage null, and expires_at 1710352418. 1710352418 minus 1710351819 is also 599 seconds. The created-time gap between those two step_001 events is 3743 seconds: 1 hour, 2 minutes, and 23 seconds. Same id, different created_at. Do not merge them. A third message-creation event uses step_001 again, created_at 1710330641, expires_at 1710331240, another 599-second gap, and completed_at 1710330642. 1710348076 minus 1710330641 is 17435 seconds: 4 hours, 50 minutes, and 35 seconds. Three created times, one id. The page does not print 3743 or 17435 (OpenAI, API definition).
The delta is a different object from the step. Its object is thread.run.step.delta. 21 characters. 21 is not 15. The schema says the step's object is always thread.run.step. The delta example is not that string. One delta carries call id call_XXNp8YGaFrjrSjgqxtC8JJ1B. 29 characters. type function. name get_current_weather. 19 characters. Later deltas append argument fragments and do not repeat the call id. An argument fragment is not a step id. An empty tool_calls array on the in_progress event is not this call. Do not paste the call id onto step_abc123 because both pages are about steps, and do not paste usage 31 onto the list example because both are completed message-creation steps.
The paper people quote when they want a count of tool calls did not open this run. Yujia Qin, Shihao Liang, Yining Ye, and Kunlun Zhu, with their coauthors, present ToolLLM. The abstract says they collect 16,464 real-world REST APIs spanning 49 categories. 16,464 divided by 49 is 336 exactly. The paper does not print 336. The filtering section says they initially gathered 10,853 tools, which is 53,190 APIs, and retained 3,451 high-quality tools, which is 16,464 APIs. 53,190 minus 16,464 is 36,726. 10,853 minus 3,451 is 7,402. The paper does not print 36,726 or 7,402. 3,451 times 4 is 13,804. 16,464 minus 13,804 is 2,660. The APIs per retained tool are not an integer the page prints. 3,451 divided by 10,853 is 0.3180 to four places, which is 31.80 percent to two places. The paper does not print that percent. They generate 126,486 instruction and solution-path pairs. 126,486 is not 16,464. A later sentence says the retriever selects from a pool of 16,000 plus APIs. 16,000 is not 16,464. The gap is 464, which the paper does not print. The title's 16000+ is not the abstract's 16,464, and neither integer is step_abc123 (Qin, Liang, Ye, and Zhu, ToolLLM).
Do the three proofs on one real step, in the log that actually holds it. First, the binding. Copy the step id, the run id, the type, and the status. If completed_at equals created_at, write that the gap is 0. If the streaming field is expires_at and the stored field is expired_at, write both names and do not rename one. If usage is present, add prompt and completion and see whether the total matches. Do not copy 123, 456, and 579 because a sample prints them, and do not copy 20, 11, and 31 because a different sample prints those. Second, the other step. A different id, or the same id with a different created_at, is not this step. step_001 appearing on three created times is that shape on the stream, and it is still a sample. A paper's API count is not that second step. Third, the other run. A list that belongs to another run does not contain this step id. If the only log is a chat scrollback, write that.
Three moves feel like the proof and are a second problem. Starting another run because the step was embarrassing, before you have copied the id. A new run gets new steps. It does not tell you which step the customer waited on. Quoting 16,464 or 336 because they look precise, while the step you were asked about is one object, and the paper that printed 16,464 had not opened your log. Asking the model which of the five statuses it was. The model will pick a plausible name. It will not open the step. A run you have not kept is which run that thread started. Opening the run does not name the step.
The path is ordinary when an operator can open one customer's steps without opening the next customer's steps as the way to prove it. Inflectiv Helios is a multi-assistant platform, distinct from the next assistant. It is not a run-step ledger. No headcount, duration, or result figure belongs in that sentence. If the only control on the screen shows a reply and not a step id, you do not have this proof. You have a transcript.
There is a version of this afternoon where hiring the path is the wrong purchase. Nothing is in production, because the feature has not reached a customer. No model is being called. The request was not logged, so there is no step to name. A customer has already seen another customer's data. Naming an id does not close a leak. The wrong value was already a write. Or no metric was ever named, so a rescue would have nothing to move. The criteria for a system that is already in front of customers and still cannot show the step sit on the page for an AI pilot failed to reach production. That page is the symptom. It does not name this run, and it does not turn 16,464 into a step id.
When the reply is already in front of a customer and the log cannot name the step, the missing object is specific. The step id, the type, and the status, a different step that is not that one, and another run's list that does not hold it. An AI pilot failed to reach production engagement is for the system that is already in front of customers and cannot yet show those three: audit in the first one to two weeks, finished in 8 to 12 weeks, scoped after the audit, unpaid if the agreed metric does not move, rollout behind a human-review gate. The step you failed to store tonight is the kind of case the harness has to include, or the next release will answer and still be unable to say which step produced it. The harness is AI evaluation and observability. An AI integration sprint is the shape where the acceptance sentence can require the step id on every stored call. If you do not yet know whether the pilot can be saved, a two-week AI readiness assessment hands you the findings either way. The free production-readiness diagnostic is the short way to see whether you are looking at a missing step, a null usage, or a system that has not reached a customer. Those are the terms. They are not 579, and they are not 336.
Before you tell them which step the run took, do the three proofs and write the id. Name the step, the type, and the status, or write that the log stored no step. Show a different step that is not that one. Show another run's list that does not hold it. Do not paste step_abc456 into a list that does not contain it, and do not paste expires_at onto a field named expired_at. A paper can tell you how many APIs were kept and how many were dropped, including the 7,402 tools it does not print. The binding is the step you can defend.
Questions
You stop treating the reply as the step the run took, and you stop asking the model which step it was, until you can name the step id, the type, and the status, show a different step whose id or type is not this one, and show that another run's list does not hold it. If the log stored no step, write that.
No. A count of other people's APIs is not the id on this step. A step you can defend is the id, the type, and the status, or a written line that the log stored no step. 16464 in one paper is not step_abc123, and a sample total of 579 tokens is not your step.
When nothing is in production, when no model is being called, when the request was not logged, when a customer has already seen another customer's data, when the wrong value was already a write, or when no metric was ever named.
Related: AI pilot failed to reach production
Find this useful? Tell Google to show you more of it.
