|
| 1 | +class_name GeminiStreamClient extends HTTPClient |
| 2 | + |
| 3 | +var _api_key |
| 4 | + |
| 5 | +signal receive_text(text) |
| 6 | +signal receive_finished |
| 7 | + |
| 8 | +var _thread = Thread.new() |
| 9 | +func _init(api_key): |
| 10 | + _api_key = api_key |
| 11 | + _thread.start(_loop_process) |
| 12 | + print("thread started") |
| 13 | + |
| 14 | +var path_text_regex = RegEx.new() |
| 15 | +func request_text(prompt): |
| 16 | + |
| 17 | + var url = "https://generativelanguage.googleapis.com/v1/models/gemini-pro:streamGenerateContent?key=%s"%_api_key |
| 18 | + |
| 19 | + var body = JSON.new().stringify({ |
| 20 | + "contents":[ |
| 21 | + { "parts":[{ |
| 22 | + "text": prompt |
| 23 | + }] |
| 24 | + } |
| 25 | + ] |
| 26 | + }) |
| 27 | + var fields = { |
| 28 | + "contents":[ |
| 29 | + { "parts":[{ |
| 30 | + "text": prompt |
| 31 | + }] |
| 32 | + } |
| 33 | + ] |
| 34 | + } |
| 35 | + |
| 36 | + print("send-content:"+str(body)) |
| 37 | + |
| 38 | + var status = self.get_status() |
| 39 | + if status!=Status.STATUS_CONNECTED: |
| 40 | + var error0 = self.connect_to_host("https://generativelanguage.googleapis.com",-1) # Connect to the host on port 80 |
| 41 | + if error0 != OK: |
| 42 | + return error0 |
| 43 | + |
| 44 | + while self.get_status() == HTTPClient.STATUS_CONNECTING or self.get_status() == HTTPClient.STATUS_RESOLVING: |
| 45 | + self.poll() |
| 46 | + if not OS.has_feature("web"): |
| 47 | + OS.delay_msec(30) |
| 48 | + else: |
| 49 | + # TODO test |
| 50 | + await Engine.get_singleton("SceneTree").create_timer(0.0).wait_one_frame() |
| 51 | + else: |
| 52 | + print("status:%s"%get_status_text(status)) |
| 53 | + assert(self.get_status() == HTTPClient.STATUS_CONNECTED) |
| 54 | + |
| 55 | + |
| 56 | + var query_string = self.query_string_from_dict(fields) |
| 57 | + |
| 58 | + var error = self.request(HTTPClient.METHOD_POST,"/v1/models/gemini-pro:streamGenerateContent?key=%s"%_api_key, ["User-Agent: Pirulo/1.0 (Godot)","Accept: */*","Content-Type: application/json"], body) |
| 59 | + |
| 60 | + return error |
| 61 | + |
| 62 | +var receiving = false |
| 63 | +var last_status = 0 |
| 64 | +func _loop_process(): |
| 65 | + while _thread: |
| 66 | + _process() |
| 67 | + OS.delay_msec(30) |
| 68 | + |
| 69 | +func _process(): |
| 70 | + var status = self.get_status() |
| 71 | + if status!=last_status: |
| 72 | + print("status:%s"%get_status_text(status)) |
| 73 | + last_status = status |
| 74 | + |
| 75 | + path_text_regex.compile(r'"parts":\s*\[\s*{\s*"text":\s*"((?:[^"\\]|\\.)*)"\s*}\s*\]') |
| 76 | + |
| 77 | + #print("status:%s"%http_client.get_status()) |
| 78 | + if self.get_status() == HTTPClient.STATUS_REQUESTING: |
| 79 | + self.poll() |
| 80 | + |
| 81 | + elif self.get_status() == HTTPClient.STATUS_BODY: |
| 82 | + #print("body") |
| 83 | + receiving = true |
| 84 | + var response_body = self.read_response_body_chunk() |
| 85 | + if response_body.size() > 0: |
| 86 | + # Process the response body |
| 87 | + var texts = response_body.get_string_from_utf8() |
| 88 | + #print(texts) |
| 89 | + var result = path_text_regex.search(texts) |
| 90 | + #print(result) |
| 91 | + if result: |
| 92 | + call_deferred("emit_signal","receive_text",result.get_string(1)) |
| 93 | + else: |
| 94 | + # possible show some remaining. |
| 95 | + print("somehow no text:%s"%texts) |
| 96 | + |
| 97 | + elif receiving and self.get_status() == HTTPClient.STATUS_CONNECTED: |
| 98 | + call_deferred("emit_signal","receive_finished") |
| 99 | + receiving = false |
| 100 | + OS.delay_msec(30) |
| 101 | + |
| 102 | +func dispose(): |
| 103 | + _thread.free() |
| 104 | + |
| 105 | +func get_status_text(status): |
| 106 | + match status: |
| 107 | + Status.STATUS_DISCONNECTED: |
| 108 | + return "Disconnected from the server." |
| 109 | + Status.STATUS_RESOLVING: |
| 110 | + return "Currently resolving the hostname for the given URL into an IP." |
| 111 | + Status.STATUS_CANT_RESOLVE: |
| 112 | + return "DNS failure: Can't resolve the hostname for the given URL." |
| 113 | + Status.STATUS_CONNECTING: |
| 114 | + return "Currently connecting to server." |
| 115 | + Status.STATUS_CANT_CONNECT: |
| 116 | + return "Can't connect to the server." |
| 117 | + Status.STATUS_CONNECTED: |
| 118 | + return "Connection established." |
| 119 | + Status.STATUS_REQUESTING: |
| 120 | + return "Currently sending request." |
| 121 | + Status.STATUS_BODY: |
| 122 | + return "HTTP body received." |
| 123 | + Status.STATUS_CONNECTION_ERROR: |
| 124 | + return "Error in HTTP connection." |
| 125 | + Status.STATUS_TLS_HANDSHAKE_ERROR: |
| 126 | + return "Error in TLS handshake." |
| 127 | + _: |
| 128 | + return "Unknown status." |
0 commit comments