mirror of
https://github.com/openai/codex.git
synced 2026-04-30 01:16:54 +00:00
Plan Agent targets before launching Genies
Teach the Agent runtime and UI to resolve target packages from installed apps and start one child Genie session per selected package, with an optional package override for debugging. Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
package com.openai.codexd
|
||||
|
||||
import java.io.IOException
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
|
||||
object CodexResponsesClient {
|
||||
fun requestText(
|
||||
context: android.content.Context,
|
||||
model: String,
|
||||
instructions: String,
|
||||
prompt: String,
|
||||
): String {
|
||||
val requestBody = buildRequest(
|
||||
model = model,
|
||||
instructions = instructions,
|
||||
prompt = prompt,
|
||||
)
|
||||
val response = CodexdLocalClient.waitForResponse(
|
||||
context = context,
|
||||
method = "POST",
|
||||
path = "/v1/responses",
|
||||
body = requestBody.toString(),
|
||||
)
|
||||
if (response.statusCode != 200) {
|
||||
throw IOException("HTTP ${response.statusCode}: ${response.body}")
|
||||
}
|
||||
return parseResponsesOutputText(response.body)
|
||||
}
|
||||
|
||||
fun buildRequest(
|
||||
model: String,
|
||||
instructions: String,
|
||||
prompt: String,
|
||||
): JSONObject {
|
||||
return JSONObject()
|
||||
.put("model", model)
|
||||
.put("store", false)
|
||||
.put("stream", true)
|
||||
.put("instructions", instructions)
|
||||
.put(
|
||||
"input",
|
||||
JSONArray().put(
|
||||
JSONObject()
|
||||
.put("role", "user")
|
||||
.put(
|
||||
"content",
|
||||
JSONArray().put(
|
||||
JSONObject()
|
||||
.put("type", "input_text")
|
||||
.put("text", prompt),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun parseResponsesOutputText(body: String): String {
|
||||
val trimmedBody = body.trim()
|
||||
if (trimmedBody.startsWith("event:") || trimmedBody.startsWith("data:")) {
|
||||
return parseResponsesStreamOutputText(trimmedBody)
|
||||
}
|
||||
return parseResponsesJsonOutputText(JSONObject(trimmedBody))
|
||||
}
|
||||
|
||||
private fun parseResponsesJsonOutputText(data: JSONObject): String {
|
||||
val directOutput = data.optString("output_text")
|
||||
if (directOutput.isNotBlank()) {
|
||||
return directOutput
|
||||
}
|
||||
val output = data.optJSONArray("output")
|
||||
?: throw IOException("Responses payload missing output")
|
||||
val combined = buildString {
|
||||
for (outputIndex in 0 until output.length()) {
|
||||
val item = output.optJSONObject(outputIndex) ?: continue
|
||||
val content = item.optJSONArray("content") ?: continue
|
||||
for (contentIndex in 0 until content.length()) {
|
||||
val part = content.optJSONObject(contentIndex) ?: continue
|
||||
if (part.optString("type") == "output_text") {
|
||||
append(part.optString("text"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (combined.isBlank()) {
|
||||
throw IOException("Responses payload missing output_text content")
|
||||
}
|
||||
return combined
|
||||
}
|
||||
|
||||
private fun parseResponsesStreamOutputText(body: String): String {
|
||||
val deltaText = StringBuilder()
|
||||
val completedItems = mutableListOf<String>()
|
||||
body.split("\n\n").forEach { rawEvent ->
|
||||
val lines = rawEvent.lineSequence().map(String::trimEnd).toList()
|
||||
if (lines.isEmpty()) {
|
||||
return@forEach
|
||||
}
|
||||
val dataPayload = lines
|
||||
.filter { it.startsWith("data:") }
|
||||
.joinToString("\n") { it.removePrefix("data:").trimStart() }
|
||||
.trim()
|
||||
if (dataPayload.isEmpty() || dataPayload == "[DONE]") {
|
||||
return@forEach
|
||||
}
|
||||
val event = JSONObject(dataPayload)
|
||||
when (event.optString("type")) {
|
||||
"response.output_text.delta" -> deltaText.append(event.optString("delta"))
|
||||
"response.output_item.done" -> {
|
||||
val item = event.optJSONObject("item") ?: return@forEach
|
||||
val content = item.optJSONArray("content") ?: return@forEach
|
||||
val text = buildString {
|
||||
for (index in 0 until content.length()) {
|
||||
val part = content.optJSONObject(index) ?: continue
|
||||
if (part.optString("type") == "output_text") {
|
||||
append(part.optString("text"))
|
||||
}
|
||||
}
|
||||
}
|
||||
if (text.isNotBlank()) {
|
||||
completedItems += text
|
||||
}
|
||||
}
|
||||
"response.failed" -> throw IOException(event.toString())
|
||||
}
|
||||
}
|
||||
if (deltaText.isNotBlank()) {
|
||||
return deltaText.toString()
|
||||
}
|
||||
val completedText = completedItems.joinToString("")
|
||||
if (completedText.isNotBlank()) {
|
||||
return completedText
|
||||
}
|
||||
throw IOException("Responses stream missing output_text content")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user