Skip to content

Commit

Permalink
fix(codegen): Adds special cases for AuthStatus to re-enable codegen (
Browse files Browse the repository at this point in the history
#5854)

Adds a ProtocolAuthStatus type to get around issues of using a boolean as a discriminator. Also refactors the code generation into separate classes for each language type and leaves all SCIP logic in Codegen.

## Test plan
Regenerated all three language types and verified that code changes were
just due to updates in the underlying types since disabling.
  • Loading branch information
jamesmcnamara authored Oct 10, 2024
1 parent 8e8756f commit ae23241
Show file tree
Hide file tree
Showing 27 changed files with 1,518 additions and 910 deletions.
38 changes: 38 additions & 0 deletions .github/workflows/agent-bindings.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Please post in #team-cody-clients if you need help getting this CI check to pass.
# Worst-case: feel free to disable this workflow here https://github.com/sourcegraph/cody/actions/workflows/agent-bindings.yml
name: agent-bindings
on:

pull_request:
paths:
- '**.ts'
- '**.tsx'
- '**.js'

jobs:
kotlin:
if: github.repository == 'sourcegraph/cody'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # SECURITY: pin third-party action hashes
id: pnpm-install
with:
version: 8.6.7
run_install: false
- name: Get pnpm store directory
id: pnpm-cache
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- run: pnpm install --frozen-lockfile
- run: pnpm generate-agent-kotlin-bindings
- run: ./agent/scripts/error-if-diff.sh
- run: ./agent/scripts/compile-bindings-if-diff.sh
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,12 @@ interface CodyAgentServer {
fun testing_autocomplete_awaitPendingVisibilityTimeout(params: Null?): CompletableFuture<CompletionItemID?>
@JsonRequest("testing/autocomplete/setCompletionVisibilityDelay")
fun testing_autocomplete_setCompletionVisibilityDelay(params: Testing_Autocomplete_SetCompletionVisibilityDelayParams): CompletableFuture<Null?>
@JsonRequest("testing/autocomplete/providerConfig")
fun testing_autocomplete_providerConfig(params: Null?): CompletableFuture<Testing_Autocomplete_ProviderConfigResult>
@JsonRequest("extensionConfiguration/change")
fun extensionConfiguration_change(params: ExtensionConfiguration): CompletableFuture<AuthStatus?>
fun extensionConfiguration_change(params: ExtensionConfiguration): CompletableFuture<ProtocolAuthStatus?>
@JsonRequest("extensionConfiguration/status")
fun extensionConfiguration_status(params: Null?): CompletableFuture<AuthStatus?>
fun extensionConfiguration_status(params: Null?): CompletableFuture<ProtocolAuthStatus?>
@JsonRequest("extensionConfiguration/getSettingsSchema")
fun extensionConfiguration_getSettingsSchema(params: Null?): CompletableFuture<String>
@JsonRequest("textDocument/change")
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ object Constants {
const val agentic = "agentic"
const val ask = "ask"
const val assistant = "assistant"
const val authenticated = "authenticated"
const val autocomplete = "autocomplete"
const val balanced = "balanced"
const val byok = "byok"
Expand All @@ -41,7 +42,6 @@ object Constants {
const val function = "function"
const val gateway = "gateway"
const val history = "history"
const val `https-example-com` = "https://example.com"
const val human = "human"
const val ignore = "ignore"
const val indentation = "indentation"
Expand Down Expand Up @@ -84,6 +84,7 @@ object Constants {
const val terminal = "terminal"
const val tree = "tree"
const val `tree-sitter` = "tree-sitter"
const val unauthenticated = "unauthenticated"
const val unified = "unified"
const val use = "use"
const val user = "user"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
@file:Suppress("FunctionName", "ClassName", "unused", "EnumEntryName", "UnusedImport")
package com.sourcegraph.cody.agent.protocol_generated;

data class Chat_RestoreParams(
val modelID: String? = null,
val messages: List<SerializedChatMessage>,
val chatID: String,
data class OrganizationsParams(
val name: String,
val id: String,
)

Original file line number Diff line number Diff line change
Expand Up @@ -8,31 +8,23 @@ import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import java.lang.reflect.Type;

sealed class AuthStatus {
sealed class ProtocolAuthStatus {
companion object {
val deserializer: JsonDeserializer<AuthStatus> =
val deserializer: JsonDeserializer<ProtocolAuthStatus> =
JsonDeserializer { element: JsonElement, _: Type, context: JsonDeserializationContext ->
if (element.getAsJsonObject().get("username") == null) {
context.deserialize<UnauthenticatedAuthStatus>(element, UnauthenticatedAuthStatus::class.java)
} else {
context.deserialize<AuthenticatedAuthStatus>(element, AuthenticatedAuthStatus::class.java)
}
when (element.getAsJsonObject().get("status").getAsString()) {
"authenticated" -> context.deserialize<ProtocolAuthenticatedAuthStatus>(element, ProtocolAuthenticatedAuthStatus::class.java)
"unauthenticated" -> context.deserialize<ProtocolUnauthenticatedAuthStatus>(element, ProtocolUnauthenticatedAuthStatus::class.java)
else -> throw Exception("Unknown discriminator ${element}")
}
}
}
}

data class UnauthenticatedAuthStatus(
val endpoint: String,
data class ProtocolAuthenticatedAuthStatus(
val status: StatusEnum, // Oneof: authenticated
val authenticated: Boolean,
val showNetworkError: Boolean? = null,
val showInvalidAccessTokenError: Boolean? = null,
val pendingValidation: Boolean,
) : AuthStatus() {
}

data class AuthenticatedAuthStatus(
val endpoint: String,
val authenticated: Boolean,
val username: String,
val isFireworksTracingEnabled: Boolean? = null,
val hasVerifiedEmail: Boolean? = null,
Expand All @@ -41,6 +33,25 @@ data class AuthenticatedAuthStatus(
val displayName: String? = null,
val avatarURL: String? = null,
val pendingValidation: Boolean,
) : AuthStatus() {
val organizations: List<OrganizationsParams>? = null,
) : ProtocolAuthStatus() {

enum class StatusEnum {
@SerializedName("authenticated") Authenticated,
}
}

data class ProtocolUnauthenticatedAuthStatus(
val status: StatusEnum, // Oneof: unauthenticated
val authenticated: Boolean,
val endpoint: String,
val showNetworkError: Boolean? = null,
val showInvalidAccessTokenError: Boolean? = null,
val pendingValidation: Boolean,
) : ProtocolAuthStatus() {

enum class StatusEnum {
@SerializedName("unauthenticated") Unauthenticated,
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ package com.sourcegraph.cody.agent.protocol_generated;

object ProtocolTypeAdapters {
fun register(gson: com.google.gson.GsonBuilder) {
gson.registerTypeAdapter(AuthStatus::class.java, AuthStatus.deserializer)
gson.registerTypeAdapter(ContextItem::class.java, ContextItem.deserializer)
gson.registerTypeAdapter(CustomCommandResult::class.java, CustomCommandResult.deserializer)
gson.registerTypeAdapter(ProtocolAuthStatus::class.java, ProtocolAuthStatus.deserializer)
gson.registerTypeAdapter(TextEdit::class.java, TextEdit.deserializer)
gson.registerTypeAdapter(WorkspaceEditOperation::class.java, WorkspaceEditOperation.deserializer)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,18 @@ data class SerializedChatMessage(
val speaker: SpeakerEnum, // Oneof: human, assistant, system
val text: String? = null,
val model: String? = null,
val intent: IntentEnum? = null, // Oneof: search, chat
) {

enum class SpeakerEnum {
@SerializedName("human") Human,
@SerializedName("assistant") Assistant,
@SerializedName("system") System,
}

enum class IntentEnum {
@SerializedName("search") Search,
@SerializedName("chat") Chat,
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ package com.sourcegraph.cody.agent.protocol_generated;
data class ServerInfo(
val name: String,
val authenticated: Boolean? = null,
val authStatus: AuthStatus? = null,
val authStatus: ProtocolAuthStatus? = null,
)

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@file:Suppress("FunctionName", "ClassName", "unused", "EnumEntryName", "UnusedImport")
package com.sourcegraph.cody.agent.protocol_generated;

data class Testing_Autocomplete_ProviderConfigResult(
val id: String,
val legacyModel: String,
val configSource: String,
)

10 changes: 5 additions & 5 deletions agent/scripts/generate-agent-kotlin-bindings.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
set -eux
INDEXER_DIR=${SCIP_TYPESCRIPT_DIR:-../scip-typescript-cody-bindings}

if [ ! -d $INDEXER_DIR ]; then
git clone https://github.com/sourcegraph/scip-typescript.git $INDEXER_DIR
if [ ! -d "$INDEXER_DIR" ]; then
git clone https://github.com/sourcegraph/scip-typescript.git "$INDEXER_DIR"
fi

pushd $INDEXER_DIR
pushd "$INDEXER_DIR"
git fetch origin
git checkout olafurpg/signatures-rebase1
git pull origin olafurpg/signatures-rebase1
Expand All @@ -16,5 +16,5 @@ popd
pnpm install --prefer-offline
pnpm build
# TODO: invoke @sourcegraph/scip-typescript npm package instead
pnpm exec ts-node $INDEXER_DIR/src/main.ts index --emit-signatures --emit-external-symbols
pnpm exec ts-node agent/src/cli/scip-codegen/command.ts --output agent/bindings/kotlin/lib/src/main/kotlin/com/sourcegraph/cody/agent/protocol_generated
pnpm exec ts-node "$INDEXER_DIR"/src/main.ts index --emit-signatures --emit-external-symbols
pnpm exec ts-node agent/src/cli/scip-codegen/command.ts --output agent/bindings/kotlin/lib/src/main/kotlin/com/sourcegraph/cody/agent/protocol_generated
2 changes: 1 addition & 1 deletion agent/scripts/test-agent-binary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ async function main() {
customHeaders: {},
})

if (!valid?.authenticated) {
if (valid?.status !== 'authenticated') {
throw new Error('Failed to authenticate')
}

Expand Down
13 changes: 8 additions & 5 deletions agent/src/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
ModelUsage,
currentAuthStatus,
currentAuthStatusAuthed,
currentAuthStatusOrNotReadyYet,
firstResultFromOperation,
telemetryRecorder,
waitUntilComplete,
Expand Down Expand Up @@ -78,6 +77,10 @@ import { AgentWorkspaceDocuments } from './AgentWorkspaceDocuments'
import { registerNativeWebviewHandlers, resolveWebviewView } from './NativeWebview'
import type { PollyRequestError } from './cli/command-jsonrpc-stdio'
import { codyPaths } from './codyPaths'
import {
currentProtocolAuthStatus,
currentProtocolAuthStatusOrNotReadyYet,
} from './currentProtocolAuthStatus'
import { AgentGlobalState } from './global-state/AgentGlobalState'
import {
MessageHandler,
Expand Down Expand Up @@ -473,7 +476,7 @@ export class Agent extends MessageHandler implements ExtensionClient {
this.registerWebviewHandlers()
}

const authStatus = currentAuthStatusOrNotReadyYet()
const authStatus = currentProtocolAuthStatusOrNotReadyYet()
return {
name: 'cody-agent',
authenticated: authStatus?.authenticated ?? false,
Expand Down Expand Up @@ -571,12 +574,12 @@ export class Agent extends MessageHandler implements ExtensionClient {
this.registerRequest('extensionConfiguration/change', async config => {
this.authenticationPromise = this.handleConfigChanges(config)
await this.authenticationPromise
return currentAuthStatus()
return currentProtocolAuthStatus()
})

this.registerRequest('extensionConfiguration/status', async () => {
await this.authenticationPromise
return currentAuthStatus()
return currentProtocolAuthStatus()
})

this.registerRequest('extensionConfiguration/getSettingsSchema', async () => {
Expand Down Expand Up @@ -994,7 +997,7 @@ export class Agent extends MessageHandler implements ExtensionClient {

this.registerAuthenticatedRequest('testing/autocomplete/providerConfig', async () => {
const provider = await vscode_shim.completionProvider()
return provider.config
return provider.config.provider
})

this.registerAuthenticatedRequest('graphql/getRepoIds', async ({ names, first }) => {
Expand Down
19 changes: 15 additions & 4 deletions agent/src/cli/scip-codegen/BaseCodegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,18 @@ export interface ProtocolSymbol {
kind: ProtocolMethodKind
}

export type ConstantType = string | boolean | number
export type ConstantTypeType = 'string' | 'boolean' | 'number'

export function typeOfUnion(union: DiscriminatedUnion): ConstantTypeType {
if (union.members.length === 0) {
throw new TypeError(`Union ${JSON.stringify(union, null, 2)} has no members`)
}
return typeof union.members[0].value as ConstantTypeType
}

export interface DiscriminatedUnionMember {
value: string
value: ConstantType
type: scip.Type
}
export interface DiscriminatedUnion {
Expand Down Expand Up @@ -212,6 +222,9 @@ export abstract class BaseCodegen {
for (const sibling of this.siblingDiscriminatedUnionProperties.get(info.symbol) ?? []) {
visitInfo(this.symtab.info(sibling))
}
if (!info.has_signature) {
return
}
if (info.signature.has_value_signature) {
visitType(info.signature.value_signature.tpe)
return
Expand Down Expand Up @@ -290,8 +303,6 @@ export abstract class BaseCodegen {
// literals. If you're hitting on this error with types like string
// literals it means you are not guarding against it higher up in the
// call stack.
// throw new TypeError(`type has no properties: ${this.debug(type)}`)
this.reporter.error('', `type has no properties: ${this.debug(type)}`)
return []
throw new TypeError(`type has no properties: ${this.debug(type)}`)
}
}
Loading

0 comments on commit ae23241

Please sign in to comment.