|
1 | 1 | #pragma once |
2 | 2 |
|
| 3 | +#include <chrono> |
3 | 4 | #include <cstddef> |
4 | 5 | #include <future> |
5 | 6 | #include <optional> |
@@ -40,6 +41,10 @@ struct InvokeResult { |
40 | 41 | json raw_response = json::object(); |
41 | 42 | }; |
42 | 43 |
|
| 44 | +struct InvokeOptions { |
| 45 | + std::optional<std::chrono::milliseconds> timeout; |
| 46 | +}; |
| 47 | + |
43 | 48 | template<bool EnableThreadSafety = false> |
44 | 49 | class BasicRuntimeFacade { |
45 | 50 | public: |
@@ -72,32 +77,101 @@ class BasicRuntimeFacade { |
72 | 77 | return describeSchema(adapter_.getToolSchemaJson(tool_name)); |
73 | 78 | } |
74 | 79 |
|
75 | | - InvokeResult invoke(InvokeRequest request) |
76 | | - { |
77 | | - return submitInvoke(std::move(request)).get(); |
78 | | - } |
79 | | - |
80 | | - std::future<InvokeResult> submitInvoke(InvokeRequest request) |
| 80 | + InvokeResult invoke(InvokeRequest request, InvokeOptions options = {}) |
81 | 81 | { |
82 | 82 | const std::string tool_name = request.tool_name; |
83 | 83 |
|
| 84 | + std::optional<task_scheduler::TaskDeadline> deadline; |
| 85 | + if (options.timeout.has_value()) |
| 86 | + { |
| 87 | + deadline = std::chrono::steady_clock::now() + *options.timeout; |
| 88 | + } |
| 89 | + |
84 | 90 | try |
85 | 91 | { |
86 | | - auto response_future = scheduler_.submitRequest(makeRequestJson(std::move(request))); |
87 | | - return std::async(std::launch::deferred, [tool_name, response_future = std::move(response_future)]() mutable { |
88 | | - try |
89 | | - { |
90 | | - return normalizeResponse(tool_name, response_future.get()); |
91 | | - } |
92 | | - catch (const json_invoke::JsonInvokeError& e) |
| 92 | + auto response_future = scheduler_.submitRequest(makeRequestJson(std::move(request)), |
| 93 | + std::move(deadline)); |
| 94 | + |
| 95 | + if (options.timeout.has_value()) |
| 96 | + { |
| 97 | + if (response_future.wait_for(*options.timeout) == std::future_status::timeout) |
93 | 98 | { |
94 | | - return makeErrorResult(tool_name, e.code(), e.what()); |
| 99 | + return makeErrorResult(tool_name, "timeout", |
| 100 | + "tool invocation exceeded timeout of " + |
| 101 | + std::to_string(options.timeout->count()) + "ms"); |
95 | 102 | } |
96 | | - catch (const std::exception& e) |
| 103 | + } |
| 104 | + |
| 105 | + try |
| 106 | + { |
| 107 | + return normalizeResponse(tool_name, response_future.get()); |
| 108 | + } |
| 109 | + catch (const std::runtime_error& e) |
| 110 | + { |
| 111 | + if (std::string(e.what()) == "task_timeout") |
97 | 112 | { |
98 | | - return makeErrorResult(tool_name, "unknown_error", e.what()); |
| 113 | + return makeErrorResult(tool_name, "timeout", |
| 114 | + "tool invocation timed out in queue"); |
99 | 115 | } |
100 | | - }); |
| 116 | + return makeErrorResult(tool_name, "unknown_error", e.what()); |
| 117 | + } |
| 118 | + catch (const json_invoke::JsonInvokeError& e) |
| 119 | + { |
| 120 | + return makeErrorResult(tool_name, e.code(), e.what()); |
| 121 | + } |
| 122 | + catch (const std::exception& e) |
| 123 | + { |
| 124 | + return makeErrorResult(tool_name, "unknown_error", e.what()); |
| 125 | + } |
| 126 | + } |
| 127 | + catch (const json_invoke::JsonInvokeError& e) |
| 128 | + { |
| 129 | + return makeErrorResult(tool_name, e.code(), e.what()); |
| 130 | + } |
| 131 | + catch (const std::exception& e) |
| 132 | + { |
| 133 | + return makeErrorResult(tool_name, "unknown_error", e.what()); |
| 134 | + } |
| 135 | + } |
| 136 | + |
| 137 | + std::future<InvokeResult> submitInvoke(InvokeRequest request, InvokeOptions options = {}) |
| 138 | + { |
| 139 | + const std::string tool_name = request.tool_name; |
| 140 | + |
| 141 | + std::optional<task_scheduler::TaskDeadline> deadline; |
| 142 | + if (options.timeout.has_value()) |
| 143 | + { |
| 144 | + deadline = std::chrono::steady_clock::now() + *options.timeout; |
| 145 | + } |
| 146 | + |
| 147 | + try |
| 148 | + { |
| 149 | + auto response_future = scheduler_.submitRequest(makeRequestJson(std::move(request)), |
| 150 | + std::move(deadline)); |
| 151 | + return std::async(std::launch::deferred, |
| 152 | + [tool_name, response_future = std::move(response_future)]() mutable { |
| 153 | + try |
| 154 | + { |
| 155 | + return normalizeResponse(tool_name, response_future.get()); |
| 156 | + } |
| 157 | + catch (const std::runtime_error& e) |
| 158 | + { |
| 159 | + if (std::string(e.what()) == "task_timeout") |
| 160 | + { |
| 161 | + return makeErrorResult(tool_name, "timeout", |
| 162 | + "tool invocation timed out in queue"); |
| 163 | + } |
| 164 | + return makeErrorResult(tool_name, "unknown_error", e.what()); |
| 165 | + } |
| 166 | + catch (const json_invoke::JsonInvokeError& e) |
| 167 | + { |
| 168 | + return makeErrorResult(tool_name, e.code(), e.what()); |
| 169 | + } |
| 170 | + catch (const std::exception& e) |
| 171 | + { |
| 172 | + return makeErrorResult(tool_name, "unknown_error", e.what()); |
| 173 | + } |
| 174 | + }); |
101 | 175 | } |
102 | 176 | catch (const json_invoke::JsonInvokeError& e) |
103 | 177 | { |
|
0 commit comments