mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-09-30 17:23:15 +00:00
fix: Remove api key check in auth, ensure thrown in loadModel (#625)
* fix: Remove api key check in auth, ensure thrown in loadModel * cr * cr
This commit is contained in:
parent
7e9733a156
commit
7a23f5fdcf
3 changed files with 53 additions and 122 deletions
|
|
@ -5,7 +5,6 @@ import {
|
|||
verifyGithubUserId,
|
||||
} from "@open-swe/shared/github/verify-user";
|
||||
import {
|
||||
API_KEY_REQUIRED_MESSAGE,
|
||||
GITHUB_INSTALLATION_ID,
|
||||
GITHUB_INSTALLATION_NAME,
|
||||
GITHUB_INSTALLATION_TOKEN_COOKIE,
|
||||
|
|
@ -18,11 +17,6 @@ import { verifyGitHubWebhookOrThrow } from "./github.js";
|
|||
import { createWithOwnerMetadata, createOwnerFilter } from "./utils.js";
|
||||
import { LANGGRAPH_USER_PERMISSIONS } from "../constants.js";
|
||||
import { getGitHubPatFromRequest } from "../utils/github-pat.js";
|
||||
import { isAllowedUser } from "@open-swe/shared/github/allowed-users";
|
||||
import { validate } from "uuid";
|
||||
import { createLogger, LogLevel } from "../utils/logger.js";
|
||||
|
||||
const logger = createLogger(LogLevel.INFO, "Auth");
|
||||
|
||||
// TODO: Export from LangGraph SDK
|
||||
export interface BaseAuthReturn {
|
||||
|
|
@ -38,64 +32,6 @@ interface AuthenticateReturn extends BaseAuthReturn {
|
|||
};
|
||||
}
|
||||
|
||||
function apiKeysInRequestBody(
|
||||
bodyStr: string | Record<string, unknown>,
|
||||
): boolean {
|
||||
logger.info("CHECKING RUN REQ BODY!!", bodyStr);
|
||||
try {
|
||||
const body = typeof bodyStr === "string" ? JSON.parse(bodyStr) : bodyStr;
|
||||
if (
|
||||
body.config?.configurable &&
|
||||
("anthropicApiKey" in body.config.configurable.apiKeys ||
|
||||
"openaiApiKey" in body.config.configurable.apiKeys ||
|
||||
"googleApiKey" in body.config.configurable.apiKeys)
|
||||
) {
|
||||
logger.info("RUN REQ BODY CONTAINS API KEYS!!", bodyStr);
|
||||
return true;
|
||||
}
|
||||
logger.info("RUN REQ BODY DOES NOT CONTAIN API KEYS!!", bodyStr);
|
||||
return false;
|
||||
} catch (e: any) {
|
||||
logger.error("RUN REQ BODY DOES NOT CONTAIN API KEYS!!", {
|
||||
bodyStr,
|
||||
error: e,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function isRunReq(reqUrl: string): boolean {
|
||||
try {
|
||||
const url = new URL(reqUrl);
|
||||
const pathnameParts = url.pathname.split("/");
|
||||
const isCreateAndWait = !!(
|
||||
pathnameParts[1] === "threads" &&
|
||||
validate(pathnameParts[2]) &&
|
||||
pathnameParts[3] === "runs" &&
|
||||
pathnameParts[4] === "wait" &&
|
||||
pathnameParts.length === 5
|
||||
);
|
||||
const isCreateBackground = !!(
|
||||
pathnameParts[1] === "threads" &&
|
||||
validate(pathnameParts[2]) &&
|
||||
pathnameParts[3] === "runs" &&
|
||||
pathnameParts.length === 4
|
||||
);
|
||||
const isCreateStream = !!(
|
||||
pathnameParts[1] === "threads" &&
|
||||
validate(pathnameParts[2]) &&
|
||||
pathnameParts[3] === "runs" &&
|
||||
pathnameParts[4] === "stream" &&
|
||||
pathnameParts.length === 5
|
||||
);
|
||||
|
||||
return !!isCreateAndWait || !!isCreateBackground || !!isCreateStream;
|
||||
} catch {
|
||||
// no-op
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export const auth = new Auth()
|
||||
.authenticate<AuthenticateReturn>(async (request: Request) => {
|
||||
const isProd = process.env.NODE_ENV === "production";
|
||||
|
|
@ -208,26 +144,6 @@ export const auth = new Auth()
|
|||
});
|
||||
}
|
||||
|
||||
const reqCopy = request.clone();
|
||||
let reqBody: string | Record<string, unknown>;
|
||||
try {
|
||||
reqBody = (await reqCopy.json()) as Record<string, unknown>;
|
||||
} catch {
|
||||
reqBody = await reqCopy.text();
|
||||
}
|
||||
if (!isAllowedUser(user.login)) {
|
||||
if (isRunReq(request.url)) {
|
||||
if (!apiKeysInRequestBody(reqBody)) {
|
||||
logger.warn("No API keys found in run request body", {
|
||||
url: request.url,
|
||||
});
|
||||
throw new HTTPException(401, {
|
||||
message: API_KEY_REQUIRED_MESSAGE,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
identity: user.id.toString(),
|
||||
is_authenticated: true,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { Task } from "./constants.js";
|
|||
import { isAllowedUser } from "@open-swe/shared/github/allowed-users";
|
||||
import { decryptSecret } from "@open-swe/shared/crypto";
|
||||
import { TASK_TO_CONFIG_DEFAULTS_MAP } from "./constants.js";
|
||||
import { API_KEY_REQUIRED_MESSAGE } from "@open-swe/shared/constants";
|
||||
|
||||
const logger = createLogger(LogLevel.INFO, "ModelManager");
|
||||
|
||||
|
|
@ -105,12 +106,55 @@ export class ModelManager {
|
|||
const model = await this.initializeModel(baseConfig, graphConfig);
|
||||
return model;
|
||||
}
|
||||
|
||||
private getUserApiKey(
|
||||
graphConfig: GraphConfig,
|
||||
provider: Provider,
|
||||
): string | null {
|
||||
const userLogin = (graphConfig.configurable as any)?.langgraph_auth_user
|
||||
?.display_name;
|
||||
const secretsEncryptionKey = process.env.SECRETS_ENCRYPTION_KEY;
|
||||
|
||||
if (!secretsEncryptionKey) {
|
||||
throw new Error(
|
||||
"SECRETS_ENCRYPTION_KEY environment variable is required",
|
||||
);
|
||||
}
|
||||
if (!userLogin) {
|
||||
throw new Error("User login not found in config");
|
||||
}
|
||||
|
||||
// If the user is allowed, we can return early
|
||||
if (isAllowedUser(userLogin)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const apiKeys = graphConfig.configurable?.apiKeys;
|
||||
if (!apiKeys) {
|
||||
throw new Error(API_KEY_REQUIRED_MESSAGE);
|
||||
}
|
||||
|
||||
const missingProviderKeyMessage = `No API key found for provider: ${provider}. Please add one in the settings page.`;
|
||||
|
||||
const providerApiKey = providerToApiKey(provider, apiKeys);
|
||||
if (!providerApiKey) {
|
||||
throw new Error(missingProviderKeyMessage);
|
||||
}
|
||||
|
||||
const apiKey = decryptSecret(providerApiKey, secretsEncryptionKey);
|
||||
if (!apiKey) {
|
||||
throw new Error(missingProviderKeyMessage);
|
||||
}
|
||||
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the model instance
|
||||
*/
|
||||
public async initializeModel(
|
||||
config: ModelLoadConfig,
|
||||
graphConfig?: GraphConfig,
|
||||
graphConfig: GraphConfig,
|
||||
) {
|
||||
const {
|
||||
provider,
|
||||
|
|
@ -130,42 +174,7 @@ export class ModelManager {
|
|||
finalMaxTokens = finalMaxTokens > 8_192 ? 8_192 : finalMaxTokens;
|
||||
}
|
||||
|
||||
let apiKey: string | null = null;
|
||||
if (graphConfig) {
|
||||
const userLogin = (graphConfig.configurable as any)?.langgraph_auth_user
|
||||
?.display_name;
|
||||
const secretsEncryptionKey = process.env.SECRETS_ENCRYPTION_KEY;
|
||||
if (!secretsEncryptionKey) {
|
||||
throw new Error(
|
||||
"SECRETS_ENCRYPTION_KEY environment variable is required",
|
||||
);
|
||||
}
|
||||
if (!userLogin) {
|
||||
throw new Error("User login not found in config");
|
||||
}
|
||||
const apiKeys = graphConfig.configurable?.apiKeys;
|
||||
if (!isAllowedUser(userLogin)) {
|
||||
if (!apiKeys) {
|
||||
throw new Error("API keys not found in config");
|
||||
}
|
||||
const providerApiKey = providerToApiKey(provider, apiKeys);
|
||||
if (!providerApiKey) {
|
||||
throw new Error(
|
||||
"No API key found for provider: " +
|
||||
provider +
|
||||
". Please add one in the settings page.",
|
||||
);
|
||||
}
|
||||
apiKey = decryptSecret(providerApiKey, secretsEncryptionKey);
|
||||
if (!apiKey) {
|
||||
throw new Error(
|
||||
"No API key found for provider: " +
|
||||
provider +
|
||||
". Please add one in the settings page.",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
const apiKey = this.getUserApiKey(graphConfig, provider);
|
||||
|
||||
const modelOptions: InitChatModelArgs = {
|
||||
modelProvider: provider,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import { ChatResult, ChatGeneration } from "@langchain/core/outputs";
|
|||
import { BaseLanguageModelInput } from "@langchain/core/language_models/base";
|
||||
import { BindToolsInput } from "@langchain/core/language_models/chat_models";
|
||||
import { getMessageContentString } from "@open-swe/shared/messages";
|
||||
import { getConfig } from "@langchain/langgraph";
|
||||
|
||||
const logger = createLogger(LogLevel.DEBUG, "FallbackRunnable");
|
||||
|
||||
|
|
@ -108,8 +109,13 @@ export class FallbackRunnable<
|
|||
continue;
|
||||
}
|
||||
|
||||
const graphConfig = getConfig() as GraphConfig;
|
||||
|
||||
try {
|
||||
const model = await this.modelManager.initializeModel(modelConfig);
|
||||
const model = await this.modelManager.initializeModel(
|
||||
modelConfig,
|
||||
graphConfig,
|
||||
);
|
||||
let runnableToUse: Runnable<BaseLanguageModelInput, AIMessageChunk> =
|
||||
model;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue