1+ use std:: error:: Error ;
2+ use dioxus:: prelude:: Signal ;
3+ use futures:: StreamExt ;
4+ use tracing:: { error, info} ;
5+ use async_openai_wasm:: Client ;
6+ use async_openai_wasm:: config:: OpenAIConfig ;
7+ use async_openai_wasm:: types:: { AssistantStreamEvent , CreateAssistantRequest , CreateAssistantRequestArgs , FunctionObject , MessageDeltaContent , RunObject , SubmitToolOutputsRunRequest , ToolsOutputs } ;
8+ use crate :: { API_BASE , API_KEY } ;
9+ use dioxus:: prelude:: * ;
10+
11+
12+ pub const TEMPERATURE : & str = "57" ;
13+ pub const RAIN_PROBABILITY : & str = "0.06" ;
14+
15+ pub fn get_client ( ) -> Client < OpenAIConfig > {
16+ let config = OpenAIConfig :: new ( )
17+ . with_api_key ( API_KEY ) ;
18+ let config = if API_BASE != "..." {
19+ config. with_api_base ( API_BASE )
20+ } else {
21+ config
22+ } ;
23+
24+ Client :: with_config ( config)
25+ }
26+
27+ pub async fn handle_requires_action ( client : & Client < OpenAIConfig > , run_object : RunObject , reply_signal : Signal < String > ) {
28+ let mut tool_outputs: Vec < ToolsOutputs > = vec ! [ ] ;
29+ if let Some ( ref required_action) = run_object. required_action {
30+ for tool in & required_action. submit_tool_outputs . tool_calls {
31+ if tool. function . name == "get_current_temperature" {
32+ info ! ( "get_current_temperature" ) ;
33+ tool_outputs. push ( ToolsOutputs {
34+ tool_call_id : Some ( tool. id . clone ( ) ) ,
35+ output : Some ( TEMPERATURE . into ( ) ) ,
36+ } )
37+ } else if tool. function . name == "get_rain_probability" {
38+ info ! ( "get_rain_probability" ) ;
39+ tool_outputs. push ( ToolsOutputs {
40+ tool_call_id : Some ( tool. id . clone ( ) ) ,
41+ output : Some ( RAIN_PROBABILITY . into ( ) ) ,
42+ } )
43+ } else {
44+ error ! ( "Unknown tool: {}" , tool. function. name) ;
45+ unreachable ! ( ) ;
46+ }
47+ }
48+
49+ if let Err ( e) = submit_tool_outputs ( client, run_object, tool_outputs, reply_signal) . await {
50+ error ! ( "Error on submitting tool outputs: {e}" ) ;
51+ }
52+ }
53+ }
54+
55+ pub async fn submit_tool_outputs (
56+ client : & Client < OpenAIConfig > ,
57+ run_object : RunObject ,
58+ tool_outputs : Vec < ToolsOutputs > ,
59+ mut reply_signal : Signal < String > ,
60+ ) -> Result < ( ) , Box < dyn Error > > {
61+ let mut event_stream = client
62+ . threads ( )
63+ . runs ( & run_object. thread_id )
64+ . submit_tool_outputs_stream (
65+ & run_object. id ,
66+ SubmitToolOutputsRunRequest {
67+ tool_outputs,
68+ stream : Some ( true ) ,
69+ } ,
70+ )
71+ . await ?;
72+
73+ while let Some ( event) = event_stream. next ( ) . await {
74+ match event {
75+ Ok ( event) => {
76+ if let AssistantStreamEvent :: ThreadMessageDelta ( delta) = event {
77+ if let Some ( contents) = delta. delta . content {
78+ for content in contents {
79+ // only text is expected here and no images
80+ if let MessageDeltaContent :: Text ( text) = content {
81+ if let Some ( text) = text. text {
82+ if let Some ( text) = text. value {
83+ info ! ( "After submitted tool results: {}" , text) ;
84+ reply_signal. with_mut ( |reply| {
85+ reply. push_str ( & text) ;
86+ } ) ;
87+ }
88+ }
89+ }
90+ }
91+ }
92+ }
93+ }
94+ Err ( e) => {
95+ error ! ( "Error: {e}" ) ;
96+ }
97+ }
98+ }
99+
100+ Ok ( ( ) )
101+ }
102+
103+ pub fn create_assistant_request ( ) -> CreateAssistantRequest {
104+ CreateAssistantRequestArgs :: default ( )
105+ . instructions ( "You are a weather bot. Use the provided functions to answer questions." )
106+ . model ( "gpt-4o" )
107+ . tools ( vec ! [
108+ FunctionObject {
109+ name: "get_current_temperature" . into( ) ,
110+ description: Some ( "Get the current temperature for a specific location" . into( ) ) ,
111+ parameters: Some ( serde_json:: json!(
112+ {
113+ "type" : "object" ,
114+ "properties" : {
115+ "location" : {
116+ "type" : "string" ,
117+ "description" : "The city and state, e.g., San Francisco, CA"
118+ } ,
119+ "unit" : {
120+ "type" : "string" ,
121+ "enum" : [ "Celsius" , "Fahrenheit" ] ,
122+ "description" : "The temperature unit to use. Infer this from the user's location."
123+ }
124+ } ,
125+ "required" : [ "location" , "unit" ]
126+ }
127+ ) ) ,
128+ } . into( ) ,
129+ FunctionObject {
130+ name: "get_rain_probability" . into( ) ,
131+ description: Some ( "Get the probability of rain for a specific location" . into( ) ) ,
132+ parameters: Some ( serde_json:: json!(
133+ {
134+ "type" : "object" ,
135+ "properties" : {
136+ "location" : {
137+ "type" : "string" ,
138+ "description" : "The city and state, e.g., San Francisco, CA"
139+ }
140+ } ,
141+ "required" : [ "location" ]
142+ }
143+ ) ) ,
144+ } . into( ) ,
145+ ] )
146+ . build ( )
147+ . expect ( "failed to build CreateAssistantRequestArgs" )
148+ }
0 commit comments