fix: save uploaded files to recipe-images/ subdir and inject source URL
Build and Deploy / build-and-deploy (push) Successful in 1m49s
Build and Deploy / build-and-deploy (push) Successful in 1m49s
This commit is contained in:
+43
-14
@@ -384,14 +384,15 @@ runFileImport recipeDir config originalName _mimeType b64Text = do
|
|||||||
Html.ImportError $
|
Html.ImportError $
|
||||||
"Could not decode uploaded file: " <> T.pack err
|
"Could not decode uploaded file: " <> T.pack err
|
||||||
Right content -> do
|
Right content -> do
|
||||||
-- Ensure recipe dir exists
|
-- Save the original file to recipe-images/ subdirectory
|
||||||
createDirectoryIfMissing True recipeDir
|
let imagesDir = recipeDir </> "recipe-images"
|
||||||
-- Save the original file
|
safeName = map (\c -> if isAlphaNum c || c == '.' || c == '-' || c == '_' then c else '_') (T.unpack originalName)
|
||||||
let sourceFilename = map (\c -> if isAlphaNum c || c == '.' || c == '-' || c == '_' then c else '_') (T.unpack originalName)
|
sourcePath = imagesDir </> safeName
|
||||||
sourcePath = recipeDir </> sourceFilename
|
sourceUrl = "/recipe-images/" <> T.pack safeName
|
||||||
|
createDirectoryIfMissing True imagesDir
|
||||||
BS.writeFile sourcePath content
|
BS.writeFile sourcePath content
|
||||||
putStrLn $ "[roux] saved uploaded file to " <> sourcePath
|
putStrLn $ "[roux] saved uploaded file to " <> sourcePath
|
||||||
-- Call the LLM pipeline with the saved file
|
-- Call the LLM pipeline with the source URL
|
||||||
case rcAnthropic config of
|
case rcAnthropic config of
|
||||||
Nothing ->
|
Nothing ->
|
||||||
pure $
|
pure $
|
||||||
@@ -399,7 +400,7 @@ runFileImport recipeDir config originalName _mimeType b64Text = do
|
|||||||
Html.ImportError $
|
Html.ImportError $
|
||||||
"LLM import is not configured. Add an anthropic.api_key to the config file."
|
"LLM import is not configured. Add an anthropic.api_key to the config file."
|
||||||
Just anthropicConfig -> do
|
Just anthropicConfig -> do
|
||||||
result <- runLLMPipeline recipeDir anthropicConfig (Just sourcePath) ""
|
result <- runLLMPipeline recipeDir anthropicConfig (Just sourceUrl) (Just sourcePath) ""
|
||||||
case result of
|
case result of
|
||||||
Right filename -> pure $ Right filename
|
Right filename -> pure $ Right filename
|
||||||
Left err -> pure $ Left err
|
Left err -> pure $ Left err
|
||||||
@@ -418,7 +419,7 @@ runTextImport recipeDir config text = do
|
|||||||
Html.ImportError $
|
Html.ImportError $
|
||||||
"LLM import is not configured. Add an anthropic.api_key to the config file."
|
"LLM import is not configured. Add an anthropic.api_key to the config file."
|
||||||
Just anthropicConfig ->
|
Just anthropicConfig ->
|
||||||
runLLMPipeline recipeDir anthropicConfig Nothing text
|
runLLMPipeline recipeDir anthropicConfig Nothing Nothing text
|
||||||
|
|
||||||
-- | Run the full LLM import pipeline: call convert-to-cooklang -> parse -> save.
|
-- | Run the full LLM import pipeline: call convert-to-cooklang -> parse -> save.
|
||||||
runLLMPipeline ::
|
runLLMPipeline ::
|
||||||
@@ -426,19 +427,21 @@ runLLMPipeline ::
|
|||||||
FilePath ->
|
FilePath ->
|
||||||
-- | API config
|
-- | API config
|
||||||
AnthropicConfig ->
|
AnthropicConfig ->
|
||||||
-- | Just path = uploaded source file to keep
|
-- | Just url = source URL to inject into Cooklang front matter
|
||||||
|
Maybe Text ->
|
||||||
|
-- | Just path = file path for the script (file upload) / Nothing = use text
|
||||||
Maybe FilePath ->
|
Maybe FilePath ->
|
||||||
-- | recipe text (or empty if file)
|
-- | recipe text (used when no file path)
|
||||||
Text ->
|
Text ->
|
||||||
IO (Either Html.ImportError FilePath)
|
IO (Either Html.ImportError FilePath)
|
||||||
runLLMPipeline recipeDir anthropicConfig mSourceFile text = do
|
runLLMPipeline recipeDir anthropicConfig mSourceUrl mFilePath text = do
|
||||||
-- Step 1: locate the script
|
-- Step 1: locate the script
|
||||||
script <- findConvertScript
|
script <- findConvertScript
|
||||||
-- Step 2: write a temp config for the subprocess
|
-- Step 2: write a temp config for the subprocess
|
||||||
let configPath = recipeDir </> ".convert-config.yaml"
|
let configPath = recipeDir </> ".convert-config.yaml"
|
||||||
writeConvertConfig configPath anthropicConfig
|
writeConvertConfig configPath anthropicConfig
|
||||||
-- Step 3: build subprocess args
|
-- Step 3: build subprocess args
|
||||||
let args = case mSourceFile of
|
let args = case mFilePath of
|
||||||
Just fp -> ["--config", configPath, "--file", fp]
|
Just fp -> ["--config", configPath, "--file", fp]
|
||||||
Nothing -> ["--config", configPath, "--text", T.unpack text]
|
Nothing -> ["--config", configPath, "--text", T.unpack text]
|
||||||
putStrLn $ "[roux] running " <> script <> " for LLM import"
|
putStrLn $ "[roux] running " <> script <> " for LLM import"
|
||||||
@@ -456,7 +459,10 @@ runLLMPipeline recipeDir anthropicConfig mSourceFile text = do
|
|||||||
"Could not convert recipe: " <> errMsg
|
"Could not convert recipe: " <> errMsg
|
||||||
ExitSuccess -> do
|
ExitSuccess -> do
|
||||||
-- Step 4: parse the Cooklang output to extract the title
|
-- Step 4: parse the Cooklang output to extract the title
|
||||||
let cooklangText = TE.decodeUtf8 (LB.toStrict stdoutBytes)
|
let rawCooklang = TE.decodeUtf8 (LB.toStrict stdoutBytes)
|
||||||
|
cooklangText = case mSourceUrl of
|
||||||
|
Just url -> injectSourceUrl rawCooklang url
|
||||||
|
Nothing -> rawCooklang
|
||||||
case parseRecipeTitle cooklangText of
|
case parseRecipeTitle cooklangText of
|
||||||
Nothing -> do
|
Nothing -> do
|
||||||
putStrLn "[roux] could not extract title from Cooklang output"
|
putStrLn "[roux] could not extract title from Cooklang output"
|
||||||
@@ -469,7 +475,7 @@ runLLMPipeline recipeDir anthropicConfig mSourceFile text = do
|
|||||||
filepath = recipeDir </> filename
|
filepath = recipeDir </> filename
|
||||||
-- Step 5: write the .cook file
|
-- Step 5: write the .cook file
|
||||||
putStrLn $ "[roux] writing imported recipe to " <> filepath
|
putStrLn $ "[roux] writing imported recipe to " <> filepath
|
||||||
LB.writeFile filepath stdoutBytes
|
BS.writeFile filepath (TE.encodeUtf8 cooklangText)
|
||||||
putStrLn $ "[roux] successfully imported recipe as " <> filename
|
putStrLn $ "[roux] successfully imported recipe as " <> filename
|
||||||
return $ Right filename
|
return $ Right filename
|
||||||
|
|
||||||
@@ -500,6 +506,29 @@ writeConvertConfig path ac = do
|
|||||||
Nothing -> []
|
Nothing -> []
|
||||||
writeFile path (unlines pairs)
|
writeFile path (unlines pairs)
|
||||||
|
|
||||||
|
{- | Insert or update the 'source:' field in Cooklang YAML front matter.
|
||||||
|
If a source field already exists, its value is replaced. Otherwise a new
|
||||||
|
source line is added before the closing '---'.
|
||||||
|
-}
|
||||||
|
injectSourceUrl :: Text -> Text -> Text
|
||||||
|
injectSourceUrl cooklang url =
|
||||||
|
case T.breakOn "---" cooklang of
|
||||||
|
(before, rest) ->
|
||||||
|
let afterFirst = T.drop 3 rest
|
||||||
|
(frontMatter, afterSecond) = T.breakOn "---" afterFirst
|
||||||
|
updated =
|
||||||
|
let (beforeSource, afterSource) = T.breakOn "source:" frontMatter
|
||||||
|
in if T.null afterSource
|
||||||
|
-- No existing source field: append one
|
||||||
|
then
|
||||||
|
let trimmed = T.stripEnd frontMatter
|
||||||
|
in trimmed <> "\nsource: " <> url <> "\n"
|
||||||
|
-- Existing source field: replace its value
|
||||||
|
else
|
||||||
|
let restOfLine = T.drop 1 (T.dropWhile (/= '\n') afterSource)
|
||||||
|
in beforeSource <> "source: " <> url <> restOfLine
|
||||||
|
in before <> "---" <> updated <> "---" <> afterSecond
|
||||||
|
|
||||||
{- | Parse the recipe title from Cooklang front matter.
|
{- | Parse the recipe title from Cooklang front matter.
|
||||||
Looks for "title: ..." in the YAML front matter block.
|
Looks for "title: ..." in the YAML front matter block.
|
||||||
-}
|
-}
|
||||||
|
|||||||
Reference in New Issue
Block a user